Only sync adapters with access can see an account - settings

This change ensures that when the user tries to toggle a sync for a
sync adapter that doesn't have access to an account we show UI for
the user to approve the sync.

bug:28163381

Change-Id: I59802df6614572cf0eaf4b72a177beb111b87b34
This commit is contained in:
Svetoslav Ganov
2016-07-11 19:34:21 -07:00
parent 124251d54e
commit 6f9bf1da90
2 changed files with 107 additions and 13 deletions

View File

@@ -28,9 +28,12 @@ import android.app.Dialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
@@ -58,7 +61,6 @@ import com.android.settingslib.RestrictedLockUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -73,6 +75,7 @@ public class AccountSyncSettings extends AccountPreferenceBase {
private static final int REALLY_REMOVE_DIALOG = 100;
private static final int FAILED_REMOVAL_DIALOG = 101;
private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
private TextView mUserId;
private TextView mProviderId;
private ImageView mProviderIcon;
@@ -234,13 +237,15 @@ public class AccountSyncSettings extends AccountPreferenceBase {
mAuthenticatorHelper.stopListeningToAccountUpdates();
}
private void addSyncStateSwitch(Account account, String authority) {
private void addSyncStateSwitch(Account account, String authority,
String packageName, int uid) {
SyncStateSwitchPreference item = (SyncStateSwitchPreference) getCachedPreference(authority);
if (item == null) {
item = new SyncStateSwitchPreference(getPrefContext(), account, authority);
item = new SyncStateSwitchPreference(getPrefContext(), account, authority,
packageName, uid);
getPreferenceScreen().addPreference(item);
} else {
item.setup(account, authority);
item.setup(account, authority, packageName, uid);
}
item.setPersistent(false);
final ProviderInfo providerInfo = getPackageManager().resolveContentProviderAsUser(
@@ -322,21 +327,57 @@ public class AccountSyncSettings extends AccountPreferenceBase {
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
final int uid = requestCode;
final int count = getPreferenceScreen().getPreferenceCount();
for (int i = 0; i < count; i++) {
Preference preference = getPreferenceScreen().getPreference(i);
if (preference instanceof SyncStateSwitchPreference) {
SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
if (syncPref.getUid() == uid) {
onPreferenceTreeClick(syncPref);
return;
}
}
}
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (getActivity() == null) {
return false;
}
if (preference instanceof SyncStateSwitchPreference) {
SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
String authority = syncPref.getAuthority();
Account account = syncPref.getAccount();
final int userId = mUserHandle.getIdentifier();
String packageName = syncPref.getPackageName();
boolean syncAutomatically = ContentResolver.getSyncAutomaticallyAsUser(account,
authority, userId);
if (syncPref.isOneTimeSyncMode()) {
// If the sync adapter doesn't have access to the account we either
// request access by starting an activity if possible or kick off the
// sync which will end up posting an access request notification.
if (requestAccountAccessIfNeeded(packageName)) {
return true;
}
requestOrCancelSync(account, authority, true);
} else {
boolean syncOn = syncPref.isChecked();
boolean oldSyncState = syncAutomatically;
if (syncOn != oldSyncState) {
// Toggling this switch triggers sync but we may need a user approval.
// If the sync adapter doesn't have access to the account we either
// request access by starting an activity if possible or kick off the
// sync which will end up posting an access request notification.
if (syncOn && requestAccountAccessIfNeeded(packageName)) {
return true;
}
// if we're enabling sync, this will request a sync as well
ContentResolver.setSyncAutomaticallyAsUser(account, authority, syncOn, userId);
// if the master sync switch is off, the request above will
@@ -353,6 +394,36 @@ public class AccountSyncSettings extends AccountPreferenceBase {
}
}
private boolean requestAccountAccessIfNeeded(String packageName) {
if (packageName == null) {
return false;
}
final int uid;
try {
uid = getContext().getPackageManager().getPackageUidAsUser(
packageName, mUserHandle.getIdentifier());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Invalid sync ", e);
return false;
}
AccountManager accountManager = getContext().getSystemService(AccountManager.class);
if (!accountManager.hasAccountAccess(mAccount, packageName, mUserHandle)) {
IntentSender intent = accountManager.createRequestAccountAccessIntentSenderAsUser(
mAccount, packageName, mUserHandle);
if (intent != null) {
try {
startIntentSenderForResult(intent, uid, null, 0, 0, 0, null);
return true;
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "Error requesting account access", e);
}
}
}
return false;
}
private void startSyncForEnabledProviders() {
requestOrCancelSyncForEnabledProviders(true /* start them */);
final Activity activity = getActivity();
@@ -520,7 +591,7 @@ public class AccountSyncSettings extends AccountPreferenceBase {
SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
mUserHandle.getIdentifier());
ArrayList<String> authorities = new ArrayList<String>();
ArrayList<SyncAdapterType> authorities = new ArrayList<>();
for (int i = 0, n = syncAdapters.length; i < n; i++) {
final SyncAdapterType sa = syncAdapters[i];
// Only keep track of sync adapters for this account
@@ -530,7 +601,7 @@ public class AccountSyncSettings extends AccountPreferenceBase {
Log.d(TAG, "updateAccountSwitches: added authority " + sa.authority
+ " to accountType " + sa.accountType);
}
authorities.add(sa.authority);
authorities.add(sa);
} else {
// keep track of invisible sync adapters, so sync now forces
// them to sync as well.
@@ -543,15 +614,23 @@ public class AccountSyncSettings extends AccountPreferenceBase {
}
cacheRemoveAllPrefs(getPreferenceScreen());
for (int j = 0, m = authorities.size(); j < m; j++) {
final String authority = authorities.get(j);
final SyncAdapterType syncAdapter = authorities.get(j);
// We could check services here....
int syncState = ContentResolver.getIsSyncableAsUser(mAccount, authority,
int syncState = ContentResolver.getIsSyncableAsUser(mAccount, syncAdapter.authority,
mUserHandle.getIdentifier());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, " found authority " + authority + " " + syncState);
Log.d(TAG, " found authority " + syncAdapter.authority + " " + syncState);
}
if (syncState > 0) {
addSyncStateSwitch(mAccount, authority);
final int uid;
try {
uid = getContext().getPackageManager().getPackageUidAsUser(
syncAdapter.getPackageName(), mUserHandle.getIdentifier());
addSyncStateSwitch(mAccount, syncAdapter.authority,
syncAdapter.getPackageName(), uid);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "No uid for package" + syncAdapter.getPackageName(), e);
}
}
}
removeCachedPrefs(getPreferenceScreen());

View File

@@ -36,6 +36,8 @@ public class SyncStateSwitchPreference extends SwitchPreference {
private boolean mFailed = false;
private Account mAccount;
private String mAuthority;
private String mPackageName;
private int mUid;
/**
* A mode for this preference where clicking does a one-time sync instead of
@@ -47,16 +49,21 @@ public class SyncStateSwitchPreference extends SwitchPreference {
super(context, attrs, 0, R.style.SyncSwitchPreference);
mAccount = null;
mAuthority = null;
mPackageName = null;
mUid = 0;
}
public SyncStateSwitchPreference(Context context, Account account, String authority) {
public SyncStateSwitchPreference(Context context, Account account, String authority,
String packageName, int uid) {
super(context, null, 0, R.style.SyncSwitchPreference);
setup(account, authority);
setup(account, authority, packageName, uid);
}
public void setup(Account account, String authority) {
public void setup(Account account, String authority, String packageName, int uid) {
mAccount = account;
mAuthority = authority;
mPackageName = packageName;
mUid = uid;
notifyChanged();
}
@@ -152,4 +159,12 @@ public class SyncStateSwitchPreference extends SwitchPreference {
public String getAuthority() {
return mAuthority;
}
public String getPackageName() {
return mPackageName;
};
public int getUid() {
return mUid;
};
}