Unlock all users before moving or migrating.
When moving apps or shared storage between storage media on FBE devices, we need all users to be unlocked to successfully move the data. This change asks the user to enter the credentials for any locked users as part of the moving/migration wizard flows. To do this we relax Utils.enforceSameOwner() to let us prompt for the credentials of unrelated users, but we carefully only extend this capability to callers interacting with the "internal" activities, which require the MANAGE_USERS permission. Test: builds, boots, users are unlocked before moving Bug: 29923055, 25861755 Change-Id: Ifaeb2557c4f8c4354e1d380eaa0e413768ee239f
This commit is contained in:
@@ -3053,6 +3053,9 @@
|
||||
\n\nDon\u2019t remove the <xliff:g id="name" example="SD card">^2</xliff:g> during the move.
|
||||
</string>
|
||||
|
||||
<!-- Body of lock screen challenge message explaining that the given user must be unlocked before data can be moved [CHAR LIMIT=64] -->
|
||||
<string name="storage_wizard_move_unlock">To move data you need to unlock user <xliff:g id="app" example="Joey">^1</xliff:g>.</string>
|
||||
|
||||
<!-- Title of wizard step showing app move progress [CHAR LIMIT=32] -->
|
||||
<string name="storage_wizard_move_progress_title">Moving <xliff:g id="app" example="Calculator">^1</xliff:g>\u2026</string>
|
||||
<!-- Body of wizard step showing app move progress [CHAR LIMIT=NONE] -->
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user