diff --git a/res/values/strings.xml b/res/values/strings.xml index 4a8e7e3ea0a..08f4fa1fedf 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3053,6 +3053,9 @@ \n\nDon\u2019t remove the ^2 during the move. + + To move data you need to unlock user ^1. + Moving ^1\u2026 diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index cab31396810..6b0a5b8b765 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -106,6 +106,7 @@ import android.widget.TabWidget; import com.android.internal.app.UnlaunchableAppActivity; import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settings.wrapper.FingerprintManagerWrapper; @@ -995,20 +996,37 @@ public final class Utils extends com.android.settingslib.Utils { } /** - * Returns the user id present in the bundle with {@link Intent#EXTRA_USER_ID} if it - * belongs to the current user. + * Returns the user id present in the bundle with + * {@link Intent#EXTRA_USER_ID} if it belongs to the current user. * - * @throws SecurityException if the given userId does not belong to the current user group. + * @throws SecurityException if the given userId does not belong to the + * current user group. */ public static int getUserIdFromBundle(Context context, Bundle bundle) { + return getUserIdFromBundle(context, bundle, false); + } + + /** + * Returns the user id present in the bundle with + * {@link Intent#EXTRA_USER_ID} if it belongs to the current user. + * + * @param isInternal indicating if the caller is "internal" to the system, + * meaning we're willing to trust extras like + * {@link ChooseLockSettingsHelper#EXTRA_ALLOW_ANY_USER}. + * @throws SecurityException if the given userId does not belong to the + * current user group. + */ + public static int getUserIdFromBundle(Context context, Bundle bundle, boolean isInternal) { if (bundle == null) { return getCredentialOwnerUserId(context); } + final boolean allowAnyUser = isInternal + && bundle.getBoolean(ChooseLockSettingsHelper.EXTRA_ALLOW_ANY_USER, false); int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId()); if (userId == LockPatternUtils.USER_FRP) { - return enforceSystemUser(context, userId); + return allowAnyUser ? userId : enforceSystemUser(context, userId); } else { - return enforceSameOwner(context, userId); + return allowAnyUser ? userId : enforceSameOwner(context, userId); } } diff --git a/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java b/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java index d9e42dff770..8c8b90e9be5 100644 --- a/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java +++ b/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java @@ -16,22 +16,28 @@ package com.android.settings.deviceinfo; +import static com.android.settings.deviceinfo.StorageSettings.TAG; + import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.os.Bundle; +import android.os.UserManager; import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.text.TextUtils; import android.util.Log; - import android.widget.Toast; + import com.android.settings.R; +import com.android.settings.password.ChooseLockSettingsHelper; import java.util.Objects; -import static com.android.settings.deviceinfo.StorageSettings.TAG; - public class StorageWizardMigrateConfirm extends StorageWizardBase { + private static final int REQUEST_CREDENTIAL = 100; + private MigrateEstimateTask mEstimate; @Override @@ -75,9 +81,22 @@ public class StorageWizardMigrateConfirm extends StorageWizardBase { @Override public void onNavigateNext() { - int moveId; + // Ensure that all users are unlocked so that we can move their data + if (StorageManager.isFileEncryptedNativeOrEmulated()) { + for (UserInfo user : getSystemService(UserManager.class).getUsers()) { + if (!StorageManager.isUserKeyUnlocked(user.id)) { + Log.d(TAG, "User " + user.id + " is currently locked; requesting unlock"); + final CharSequence description = TextUtils.expandTemplate( + getText(R.string.storage_wizard_move_unlock), user.name); + new ChooseLockSettingsHelper(this).launchConfirmationActivityForAnyUser( + REQUEST_CREDENTIAL, null, null, description, user.id); + return; + } + } + } // We only expect exceptions from StorageManagerService#setPrimaryStorageUuid + int moveId; try { moveId = getPackageManager().movePrimaryStorage(mVolume); } catch (IllegalArgumentException e) { @@ -108,4 +127,22 @@ public class StorageWizardMigrateConfirm extends StorageWizardBase { startActivity(intent); finishAffinity(); } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CREDENTIAL) { + if (resultCode == RESULT_OK) { + // Credentials confirmed, so storage should be unlocked; let's + // go look for the next locked user. + onNavigateNext(); + } else { + // User wasn't able to confirm credentials, so we're okay + // landing back at the wizard page again, where they read + // instructions again and tap "Next" to try again. + Log.w(TAG, "Failed to confirm credentials"); + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } } diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java index 65b3d1f04a5..e82612c4f98 100644 --- a/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java +++ b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java @@ -16,20 +16,30 @@ package com.android.settings.deviceinfo; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; - -import com.android.internal.util.Preconditions; -import com.android.settings.R; - import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.Intent.EXTRA_TITLE; import static android.content.pm.PackageManager.EXTRA_MOVE_ID; import static android.os.storage.VolumeInfo.EXTRA_VOLUME_ID; +import static com.android.settings.deviceinfo.StorageSettings.TAG; + +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; +import android.os.Bundle; +import android.os.UserManager; +import android.os.storage.StorageManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.util.Preconditions; +import com.android.settings.R; +import com.android.settings.password.ChooseLockSettingsHelper; + public class StorageWizardMoveConfirm extends StorageWizardBase { + private static final int REQUEST_CREDENTIAL = 100; + private String mPackageName; private ApplicationInfo mApp; @@ -66,6 +76,20 @@ public class StorageWizardMoveConfirm extends StorageWizardBase { @Override public void onNavigateNext() { + // Ensure that all users are unlocked so that we can move their data + if (StorageManager.isFileEncryptedNativeOrEmulated()) { + for (UserInfo user : getSystemService(UserManager.class).getUsers()) { + if (!StorageManager.isUserKeyUnlocked(user.id)) { + Log.d(TAG, "User " + user.id + " is currently locked; requesting unlock"); + final CharSequence description = TextUtils.expandTemplate( + getText(R.string.storage_wizard_move_unlock), user.name); + new ChooseLockSettingsHelper(this).launchConfirmationActivityForAnyUser( + REQUEST_CREDENTIAL, null, null, description, user.id); + return; + } + } + } + // Kick off move before we transition final String appName = getPackageManager().getApplicationLabel(mApp).toString(); final int moveId = getPackageManager().movePackage(mPackageName, mVolume); @@ -77,4 +101,22 @@ public class StorageWizardMoveConfirm extends StorageWizardBase { startActivity(intent); finishAffinity(); } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CREDENTIAL) { + if (resultCode == RESULT_OK) { + // Credentials confirmed, so storage should be unlocked; let's + // go look for the next locked user. + onNavigateNext(); + } else { + // User wasn't able to confirm credentials, so we're okay + // landing back at the wizard page again, where they read + // instructions again and tap "Next" to try again. + Log.w(TAG, "Failed to confirm credentials"); + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } } diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java index 806ee67ff3b..11d40a3419d 100644 --- a/src/com/android/settings/password/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java @@ -23,6 +23,7 @@ import android.app.KeyguardManager; import android.app.admin.DevicePolicyManager; import android.content.Intent; import android.content.IntentSender; +import android.os.Bundle; import android.os.UserManager; import com.android.internal.annotations.VisibleForTesting; @@ -42,6 +43,12 @@ public final class ChooseLockSettingsHelper { public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint"; public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot"; + /** + * When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag + * controls if we relax the enforcement of + * {@link Utils#enforceSameOwner(android.content.Context, int)}. + */ + public static final String EXTRA_ALLOW_ANY_USER = "allow_any_user"; @VisibleForTesting LockPatternUtils mLockPatternUtils; private Activity mActivity; @@ -200,25 +207,47 @@ public final class ChooseLockSettingsHelper { external, true, challenge, Utils.enforceSameOwner(mActivity, userId)); } + /** + * Variant that allows you to prompt for credentials of any user, including + * those which aren't associated with the current user. As an example, this + * is useful when unlocking the storage for secondary users. + */ + public boolean launchConfirmationActivityForAnyUser(int request, + @Nullable CharSequence title, @Nullable CharSequence header, + @Nullable CharSequence description, int userId) { + final Bundle extras = new Bundle(); + extras.putBoolean(EXTRA_ALLOW_ANY_USER, true); + return launchConfirmationActivity(request, title, header, description, false, + false, true, 0, userId, extras); + } + private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external, boolean hasChallenge, long challenge, int userId) { return launchConfirmationActivity(request, title, header, description, returnCredentials, - external, hasChallenge, challenge, userId, null /* alternateButton */); + external, hasChallenge, challenge, userId, null /* alternateButton */, null); + } + + private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, + @Nullable CharSequence header, @Nullable CharSequence description, + boolean returnCredentials, boolean external, boolean hasChallenge, + long challenge, int userId, Bundle extras) { + return launchConfirmationActivity(request, title, header, description, returnCredentials, + external, hasChallenge, challenge, userId, null /* alternateButton */, extras); } public boolean launchFrpConfirmationActivity(int request, @Nullable CharSequence header, @Nullable CharSequence description, @Nullable CharSequence alternateButton) { return launchConfirmationActivity(request, null /* title */, header, description, false /* returnCredentials */, true /* external */, false /* hasChallenge */, - 0 /* challenge */, LockPatternUtils.USER_FRP, alternateButton); + 0 /* challenge */, LockPatternUtils.USER_FRP, alternateButton, null); } private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external, boolean hasChallenge, - long challenge, int userId, @Nullable CharSequence alternateButton) { + long challenge, int userId, @Nullable CharSequence alternateButton, Bundle extras) { final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId); boolean launched = false; @@ -228,7 +257,7 @@ public final class ChooseLockSettingsHelper { returnCredentials || hasChallenge ? ConfirmLockPattern.InternalActivity.class : ConfirmLockPattern.class, returnCredentials, external, - hasChallenge, challenge, userId, alternateButton); + hasChallenge, challenge, userId, alternateButton, extras); break; case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: @@ -240,7 +269,7 @@ public final class ChooseLockSettingsHelper { returnCredentials || hasChallenge ? ConfirmLockPassword.InternalActivity.class : ConfirmLockPassword.class, returnCredentials, external, - hasChallenge, challenge, userId, alternateButton); + hasChallenge, challenge, userId, alternateButton, extras); break; } return launched; @@ -249,7 +278,7 @@ public final class ChooseLockSettingsHelper { private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header, CharSequence message, Class activityClass, boolean returnCredentials, boolean external, boolean hasChallenge, long challenge, - int userId, @Nullable CharSequence alternateButton) { + int userId, @Nullable CharSequence alternateButton, Bundle extras) { final boolean frp = (userId == LockPatternUtils.USER_FRP); final Intent intent = new Intent(); intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title); @@ -266,6 +295,9 @@ public final class ChooseLockSettingsHelper { intent.putExtra(SettingsActivity.EXTRA_HIDE_DRAWER, true); intent.putExtra(Intent.EXTRA_USER_ID, userId); intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton); + if (extras != null) { + intent.putExtras(extras); + } intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName()); if (external) { intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseActivity.java index d2bd934b7c1..ab8c3dc4312 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseActivity.java @@ -45,10 +45,15 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi private boolean mIsKeyguardLocked = false; private ConfirmCredentialTheme mConfirmCredentialTheme; + private boolean isInternalActivity() { + return (this instanceof ConfirmLockPassword.InternalActivity) + || (this instanceof ConfirmLockPattern.InternalActivity); + } + @Override protected void onCreate(Bundle savedState) { int credentialOwnerUserId = Utils.getCredentialOwnerUserId(this, - Utils.getUserIdFromBundle(this, getIntent().getExtras())); + Utils.getUserIdFromBundle(this, getIntent().getExtras(), isInternalActivity())); if (UserManager.get(this).isManagedProfile(credentialOwnerUserId)) { setTheme(R.style.Theme_ConfirmDeviceCredentialsWork); mConfirmCredentialTheme = ConfirmCredentialTheme.WORK; diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java index 350fc76cef7..5b18925f1c7 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java @@ -94,6 +94,11 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedPr protected boolean mFrp; private CharSequence mFrpAlternateButtonText; + private boolean isInternalActivity() { + return (getActivity() instanceof ConfirmLockPassword.InternalActivity) + || (getActivity() instanceof ConfirmLockPattern.InternalActivity); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -103,7 +108,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedPr ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, false); // Only take this argument into account if it belongs to the current profile. Intent intent = getActivity().getIntent(); - mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras()); + mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(), + isInternalActivity()); mFrp = (mUserId == LockPatternUtils.USER_FRP); mUserManager = UserManager.get(getActivity()); mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId); @@ -141,7 +147,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedPr getActivity(), Utils.getUserIdFromBundle( getActivity(), - getActivity().getIntent().getExtras())); + getActivity().getIntent().getExtras(), isInternalActivity())); if (mUserManager.isManagedProfile(credentialOwnerUserId)) { setWorkChallengeBackground(view, credentialOwnerUserId); }