diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 82118168d9b..d03305dc916 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. * - *
This takes into account the id of the user that triggered the settings screen.
+ * 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.
+ *
+ * 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 6125adca226..cfe2cb2902c 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 e60bed93fa3..13878d7f445 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