diff options
author | Alexandra Gherghina <alexgherghina@google.com> | 2014-06-27 12:33:42 +0100 |
---|---|---|
committer | Alexandra Gherghina <alexgherghina@google.com> | 2014-07-17 19:05:57 +0000 |
commit | 7d748c0267b7d499309464ebae168be01e136b4c (patch) | |
tree | 9c00c859437be85213a59fd1afadc4774df6020f /src | |
parent | d888b543b2c188e59ea537ba4e73bf9253fbfc93 (diff) | |
download | packages_apps_Settings-7d748c0267b7d499309464ebae168be01e136b4c.zip packages_apps_Settings-7d748c0267b7d499309464ebae168be01e136b4c.tar.gz packages_apps_Settings-7d748c0267b7d499309464ebae168be01e136b4c.tar.bz2 |
Handle choosing and adding accounts for a managed profile
The user id can now be passed through the app as a fragment argument.
Bug: 15466880
Change-Id: I0e2be20551b4ec8c9226640ac74ea74115156ccd
Diffstat (limited to 'src')
7 files changed, 170 insertions, 38 deletions
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 8211816..d03305d 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -16,7 +16,11 @@ package com.android.settings; +import static android.content.Intent.EXTRA_USER; + +import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.app.Dialog; import android.app.Fragment; @@ -61,6 +65,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.TabWidget; + import com.android.settings.dashboard.DashboardCategory; import com.android.settings.dashboard.DashboardTile; @@ -73,6 +78,7 @@ import java.util.Locale; public final class Utils { private static final String TAG = "Settings"; + /** * Set the preference's title to the matching activity's label. */ @@ -110,6 +116,8 @@ public final class Utils { */ private static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary"; + private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; + /** * Finds a matching activity for a preference's intent. If a matching * activity is not found, it will remove the preference. @@ -613,28 +621,100 @@ public final class Utils { } /** - * Returns the {@link UserHandle} of the profile that a settings screen should refer to. + * Returns the target user for a Settings activity. + * + * The target user can be either the current user, the user that launched this activity or + * the user contained as an extra in the arguments or intent extras. * - * <p> This takes into account the id of the user that triggered the settings screen. + * Note: This is secure in the sense that it only returns a target user different to the current + * one if the app launching this activity is the Settings app itself, running in the same user + * or in one that is in the same profile group, or if the user id is provided by the system. */ - public static UserHandle getProfileToDisplay(IActivityManager am, IBinder activityToken, - Bundle arguments) { - int currentUser = UserHandle.getCallingUserId(); - // Check to see if it was called from a different user + public static UserHandle getSecureTargetUser(IBinder activityToken, + UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) { + UserHandle currentUser = new UserHandle(UserHandle.myUserId()); + IActivityManager am = ActivityManagerNative.getDefault(); + try { + String launchedFromPackage = am.getLaunchedFromPackage(activityToken); + boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage); + + UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId( + am.getLaunchedFromUid(activityToken))); + if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) { + // Check it's secure + if (isProfileOf(um, launchedFromUser)) { + return launchedFromUser; + } + } + UserHandle extrasUser = intentExtras != null + ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null; + if (extrasUser != null && !extrasUser.equals(currentUser)) { + // Check it's secure + if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) { + return extrasUser; + } + } + UserHandle argumentsUser = arguments != null + ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null; + if (argumentsUser != null && !argumentsUser.equals(currentUser)) { + // Check it's secure + if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) { + return argumentsUser; + } + } + } catch (RemoteException e) { + // Should not happen + Log.v(TAG, "Could not talk to activity manager.", e); + } + return currentUser; + } + + /** + * Returns the target user for a Settings activity. + * + * The target user can be either the current user, the user that launched this activity or + * the user contained as an extra in the arguments or intent extras. + * + * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if + * possible. + * + * @see #getInsecureTargetUser(IBinder, Bundle, Bundle) + */ + public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments, + @Nullable Bundle intentExtras) { + UserHandle currentUser = new UserHandle(UserHandle.myUserId()); + IActivityManager am = ActivityManagerNative.getDefault(); try { - int launchedFromUser = UserHandle.getUserId(am.getLaunchedFromUid(activityToken)); - if (launchedFromUser != currentUser) { - // This is a forwarded intent - return new UserHandle(launchedFromUser); + UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId( + am.getLaunchedFromUid(activityToken))); + if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) { + return launchedFromUser; + } + UserHandle extrasUser = intentExtras != null + ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null; + if (extrasUser != null && !extrasUser.equals(currentUser)) { + return extrasUser; + } + UserHandle argumentsUser = arguments != null + ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null; + if (argumentsUser != null && !argumentsUser.equals(currentUser)) { + return argumentsUser; } } catch (RemoteException e) { // Should not happen - Log.v(TAG, "Could not get launching user."); + Log.v(TAG, "Could not talk to activity manager.", e); + return null; } - // TODO: Check fragment arguments. See: http://b/15466880 + return currentUser; + } - // Default to current profile - return new UserHandle(currentUser); + /** + * Returns true if the user provided is in the same profiles group as the current user. + */ + private static boolean isProfileOf(UserManager um, UserHandle otherUser) { + if (um == null || otherUser == null) return false; + return (UserHandle.myUserId() == otherUser.getIdentifier()) + || um.getUserProfiles().contains(otherUser); } /** diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java index 6125adc..cfe2cb2 100644 --- a/src/com/android/settings/accounts/AccountPreferenceBase.java +++ b/src/com/android/settings/accounts/AccountPreferenceBase.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +22,7 @@ import com.google.android.collect.Maps; import android.accounts.AuthenticatorDescription; import android.app.Activity; import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.SyncAdapterType; @@ -68,9 +70,10 @@ class AccountPreferenceBase extends SettingsPreferenceFragment public void onCreate(Bundle icicle) { super.onCreate(icicle); mUm = (UserManager) getSystemService(Context.USER_SERVICE); - mUserHandle = Utils.getProfileToDisplay(ActivityManagerNative.getDefault(), - getActivity().getActivityToken(), icicle); - mAuthenticatorHelper = new AuthenticatorHelper(getActivity(), mUserHandle, mUm, this); + final Activity activity = getActivity(); + mUserHandle = Utils.getSecureTargetUser(activity.getActivityToken(), mUm, getArguments(), + activity.getIntent().getExtras()); + mAuthenticatorHelper = new AuthenticatorHelper(activity, mUserHandle, mUm, this); } /** @@ -172,8 +175,8 @@ class AccountPreferenceBase extends SettingsPreferenceFragment // correct text colors. Control colors will still be wrong, // but there's not much we can do about it since we can't // reference local color resources. - final Context targetCtx = getActivity().createPackageContext( - desc.packageName, 0); + final Context targetCtx = getActivity().createPackageContextAsUser( + desc.packageName, 0, mUserHandle); final Theme baseTheme = getResources().newTheme(); baseTheme.applyStyle(com.android.settings.R.style.Theme_SettingsBase, true); final Context themedCtx = new ContextThemeWrapper(targetCtx, 0); diff --git a/src/com/android/settings/accounts/AccountSettings.java b/src/com/android/settings/accounts/AccountSettings.java index e60bed9..13878d7 100644 --- a/src/com/android/settings/accounts/AccountSettings.java +++ b/src/com/android/settings/accounts/AccountSettings.java @@ -16,14 +16,13 @@ package com.android.settings.accounts; + import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.OnAccountsUpdateListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; @@ -39,13 +38,17 @@ import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import static android.content.Intent.EXTRA_USER; + /** * Settings screen for the account types on the device. * This shows all account types available for personal and work profiles. + * + * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for + * which the action needs to be performed is different to the one the Settings App will run in. */ public class AccountSettings extends SettingsPreferenceFragment implements AuthenticatorHelper.OnAccountsUpdateListener, @@ -146,7 +149,7 @@ public class AccountSettings extends SettingsPreferenceFragment } final ProfileData profileData = new ProfileData(); profileData.preferenceGroup = (PreferenceGroup) findPreference(categoryKey); - if (mUm.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { + if (mUm.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, userHandle)) { removePreference(addAccountKey); } else { profileData.addAccountPreference = findPreference(addAccountKey); @@ -189,7 +192,7 @@ public class AccountSettings extends SettingsPreferenceFragment private void updateAccountTypes(ProfileData profileData) { profileData.preferenceGroup.removeAll(); final ArrayList<AccountPreference> preferences = getAccountTypePreferences( - profileData.authenticatorHelper); + profileData.authenticatorHelper, profileData.userHandle); final int count = preferences.size(); for (int i = 0; i < count; i++) { profileData.preferenceGroup.addPreference(preferences.get(i)); @@ -199,7 +202,8 @@ public class AccountSettings extends SettingsPreferenceFragment } } - private ArrayList<AccountPreference> getAccountTypePreferences(AuthenticatorHelper helper) { + private ArrayList<AccountPreference> getAccountTypePreferences(AuthenticatorHelper helper, + UserHandle userHandle) { final String[] accountTypes = helper.getEnabledAccountTypes(); final ArrayList<AccountPreference> accountTypePreferences = new ArrayList<AccountPreference>(accountTypes.length); @@ -212,7 +216,7 @@ public class AccountSettings extends SettingsPreferenceFragment } final Account[] accounts = AccountManager.get(getActivity()) - .getAccountsByType(accountType); + .getAccountsByTypeAsUser(accountType, userHandle); final boolean skipToAccount = accounts.length == 1 && !helper.hasAccountPreferences(accountType); @@ -220,6 +224,7 @@ public class AccountSettings extends SettingsPreferenceFragment final Bundle fragmentArguments = new Bundle(); fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); + fragmentArguments.putParcelable(EXTRA_USER, userHandle); accountTypePreferences.add(new AccountPreference(getActivity(), label, AccountSyncSettings.class.getName(), fragmentArguments, @@ -229,6 +234,7 @@ public class AccountSettings extends SettingsPreferenceFragment fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, label.toString()); + fragmentArguments.putParcelable(EXTRA_USER, userHandle); accountTypePreferences.add(new AccountPreference(getActivity(), label, ManageAccountsSettings.class.getName(), fragmentArguments, @@ -254,7 +260,7 @@ public class AccountSettings extends SettingsPreferenceFragment ProfileData profileData = mProfiles.valueAt(i); if (preference == profileData.addAccountPreference) { Intent intent = new Intent(ADD_ACCOUNT_ACTION); - intent.putExtra(Intent.EXTRA_USER_HANDLE, profileData.userHandle); + intent.putExtra(EXTRA_USER, profileData.userHandle); startActivity(intent); return true; } diff --git a/src/com/android/settings/accounts/AccountSyncSettings.java b/src/com/android/settings/accounts/AccountSyncSettings.java index 458adca..1bde8a3 100644 --- a/src/com/android/settings/accounts/AccountSyncSettings.java +++ b/src/com/android/settings/accounts/AccountSyncSettings.java @@ -90,6 +90,8 @@ public class AccountSyncSettings extends AccountPreferenceBase { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + // TODO: We need an API to remove an account from a different user. + // See: http://b/15466880 AccountManager.get(AccountSyncSettings.this.getActivity()) .removeAccount(mAccount, new AccountManagerCallback<Boolean>() { diff --git a/src/com/android/settings/accounts/AddAccountSettings.java b/src/com/android/settings/accounts/AddAccountSettings.java index 34b6c8d..0af0260 100644 --- a/src/com/android/settings/accounts/AddAccountSettings.java +++ b/src/com/android/settings/accounts/AddAccountSettings.java @@ -22,22 +22,25 @@ import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; +import android.app.ActivityManagerNative; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import android.widget.Toast; import com.android.settings.R; -import com.android.settings.Settings; import com.android.settings.Utils; import java.io.IOException; +import static android.content.Intent.EXTRA_USER; + /** - * Entry point Actiivty for account setup. Works as follows + * Entry point Activity for account setup. Works as follows * * 1) When the other Activities launch this Activity, it launches {@link ChooseAccountActivity} * without showing anything. @@ -50,6 +53,9 @@ import java.io.IOException; * currently delegate the work to the other Activity. When we let this Activity do that work, users * would see the list of account types when leaving this Activity, since the UI is already ready * when returning from each account setup, which doesn't look good. + * + * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for + * which the action needs to be performed is different to the one the Settings App will run in. */ public class AddAccountSettings extends Activity { /** @@ -90,6 +96,7 @@ public class AddAccountSettings extends Activity { addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(AddAccountSettings.this)); + addAccountOptions.putParcelable(EXTRA_USER, mUserHandle); intent.putExtras(addAccountOptions); startActivityForResult(intent, ADD_ACCOUNT_REQUEST); } else { @@ -116,6 +123,7 @@ public class AddAccountSettings extends Activity { }; private boolean mAddAccountCalled = false; + private UserHandle mUserHandle; @Override public void onCreate(Bundle savedInstanceState) { @@ -127,7 +135,9 @@ public class AddAccountSettings extends Activity { } final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { + mUserHandle = Utils.getSecureTargetUser(getActivityToken(), um, null /* arguments */, + getIntent().getExtras()); + if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle)) { // We aren't allowed to add an account. Toast.makeText(this, R.string.user_cannot_add_accounts_message, Toast.LENGTH_LONG) .show(); @@ -150,6 +160,7 @@ public class AddAccountSettings extends Activity { if (accountTypes != null) { intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes); } + intent.putExtra(EXTRA_USER, mUserHandle); startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST); } @@ -188,6 +199,14 @@ public class AddAccountSettings extends Activity { mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(), 0); addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this)); + // TODO: We need an API to add an account to a different user. See: http://b/15466880 + int userId = mUserHandle.getIdentifier(); + int callingUserId = UserHandle.getCallingUserId(); + if (userId != callingUserId) { + Log.w(TAG, "Cannot add an account for user " + userId + " from " + callingUserId + "."); + finish(); + return; + } AccountManager.get(this).addAccount( accountType, null, /* authTokenType */ diff --git a/src/com/android/settings/accounts/ChooseAccountActivity.java b/src/com/android/settings/accounts/ChooseAccountActivity.java index 631fe47..7c0dbdb 100644 --- a/src/com/android/settings/accounts/ChooseAccountActivity.java +++ b/src/com/android/settings/accounts/ChooseAccountActivity.java @@ -18,6 +18,7 @@ package com.android.settings.accounts; import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; +import android.app.ActivityManagerNative; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -26,13 +27,18 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; + import com.android.internal.util.CharSequences; import com.android.settings.R; +import com.android.settings.Utils; + import com.google.android.collect.Maps; import java.util.ArrayList; @@ -41,8 +47,13 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import static android.content.Intent.EXTRA_USER; + /** * Activity asking a user to select an account to be set up. + * + * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for + * which the action needs to be performed is different to the one the Settings App will run in. */ public class ChooseAccountActivity extends PreferenceActivity { @@ -55,7 +66,10 @@ public class ChooseAccountActivity extends PreferenceActivity { private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null; private Map<String, AuthenticatorDescription> mTypeToAuthDescription = new HashMap<String, AuthenticatorDescription>(); - + // The UserHandle of the user we are choosing an account for + private UserHandle mUserHandle; + private UserManager mUm; + private static class ProviderEntry implements Comparable<ProviderEntry> { private final CharSequence name; private final String type; @@ -92,6 +106,9 @@ public class ChooseAccountActivity extends PreferenceActivity { } } mAddAccountGroup = getPreferenceScreen(); + mUm = UserManager.get(this); + mUserHandle = Utils.getSecureTargetUser(getActivityToken(), mUm, null /* arguments */, + getIntent().getExtras()); updateAuthDescriptions(); } @@ -100,7 +117,8 @@ public class ChooseAccountActivity extends PreferenceActivity { * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated(). */ private void updateAuthDescriptions() { - mAuthDescs = AccountManager.get(this).getAuthenticatorTypes(); + mAuthDescs = AccountManager.get(this).getAuthenticatorTypesAsUser( + mUserHandle.getIdentifier()); for (int i = 0; i < mAuthDescs.length; i++) { mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); } @@ -168,7 +186,8 @@ public class ChooseAccountActivity extends PreferenceActivity { public ArrayList<String> getAuthoritiesForAccountType(String type) { if (mAccountTypeToAuthorities == null) { mAccountTypeToAuthorities = Maps.newHashMap(); - SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes(); + SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( + mUserHandle.getIdentifier()); for (int i = 0, n = syncAdapters.length; i < n; i++) { final SyncAdapterType sa = syncAdapters[i]; ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType); @@ -196,8 +215,9 @@ public class ChooseAccountActivity extends PreferenceActivity { if (mTypeToAuthDescription.containsKey(accountType)) { try { AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); - Context authContext = createPackageContext(desc.packageName, 0); - icon = authContext.getResources().getDrawable(desc.iconId); + Context authContext = createPackageContextAsUser(desc.packageName, 0, mUserHandle); + icon = mUm.getBadgedDrawableForUser( + authContext.getResources().getDrawable(desc.iconId), mUserHandle); } catch (PackageManager.NameNotFoundException e) { // TODO: place holder icon for missing account icons? Log.w(TAG, "No icon name for account type " + accountType); @@ -219,7 +239,7 @@ public class ChooseAccountActivity extends PreferenceActivity { if (mTypeToAuthDescription.containsKey(accountType)) { try { AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); - Context authContext = createPackageContext(desc.packageName, 0); + Context authContext = createPackageContextAsUser(desc.packageName, 0, mUserHandle); label = authContext.getResources().getText(desc.labelId); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "No label name for account type " + accountType); @@ -245,6 +265,7 @@ public class ChooseAccountActivity extends PreferenceActivity { private void finishWithAccountType(String accountType) { Intent intent = new Intent(); intent.putExtra(AddAccountSettings.EXTRA_SELECTED_ACCOUNT, accountType); + intent.putExtra(EXTRA_USER, mUserHandle); setResult(RESULT_OK, intent); finish(); } diff --git a/src/com/android/settings/bluetooth/MessageAccessSettings.java b/src/com/android/settings/bluetooth/MessageAccessSettings.java index bef59c8..913357c 100644 --- a/src/com/android/settings/bluetooth/MessageAccessSettings.java +++ b/src/com/android/settings/bluetooth/MessageAccessSettings.java @@ -54,8 +54,9 @@ public class MessageAccessSettings extends SettingsPreferenceFragment @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mUserHandle = Utils.getProfileToDisplay(ActivityManagerNative.getDefault(), - getActivity().getActivityToken(), savedInstanceState); + + // TODO: Define behavior for managed profile. See: http://b/16287773 + mUserHandle = new UserHandle(UserHandle.myUserId()); addPreferencesFromResource(R.xml.bluetooth_message_access); } |