DO NOT MERGE: Fix DPM.ACTION_SET_NEW_PASSWORD

This is a cherry-pick of ag/1640561.

1. Context.getActivityToken is not available in n mr2, passed the token
   from the activity to the controller instead.

2. Haven't pick the tests from master to mr2 branch because robotest
   is not set up in mr2 branch.

Fix: 32959373

Change-Id: I2f73d01ab11e91b337beb90c05bbcb857dfd40dc
This commit is contained in:
Tony Mak
2016-12-12 02:29:55 +00:00
parent c04f8b9ecd
commit 8a2f28a629
6 changed files with 199 additions and 60 deletions

View File

@@ -186,22 +186,15 @@ public class ChooseLockGeneric extends SettingsActivity {
ENCRYPT_REQUESTED_DISABLED);
}
int targetUser = Utils.getSecureTargetUser(
// a) If this is started from other user, use that user id.
// b) If this is started from the same user, read the extra if this is launched
// from Settings app itself.
// c) Otherwise, use UserHandle.myUserId().
mUserId = Utils.getSecureTargetUser(
getActivity().getActivityToken(),
UserManager.get(getActivity()),
null,
getArguments(),
getActivity().getIntent().getExtras()).getIdentifier();
if (ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction)
|| !mLockPatternUtils.isSeparateProfileChallengeAllowed(targetUser)) {
// Always use parent if explicitely requested or if profile challenge is not
// supported
Bundle arguments = getArguments();
mUserId = Utils.getUserIdFromBundle(getContext(), arguments != null ? arguments
: getActivity().getIntent().getExtras());
} else {
mUserId = targetUser;
}
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
&& Utils.isManagedProfile(UserManager.get(getActivity()), mUserId)
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -256,6 +249,8 @@ public class ChooseLockGeneric extends SettingsActivity {
} else if (KEY_SKIP_FINGERPRINT.equals(key)) {
Intent chooseLockGenericIntent = new Intent(getActivity(), ChooseLockGeneric.class);
chooseLockGenericIntent.setAction(getIntent().getAction());
// Forward the target user id to ChooseLockGeneric.
chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
chooseLockGenericIntent.putExtra(PASSWORD_CONFIRMED, mPasswordConfirmed);
startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST);
return true;
@@ -343,6 +338,8 @@ public class ChooseLockGeneric extends SettingsActivity {
if (data != null) {
intent.putExtras(data.getExtras());
}
// Forward the target user id to fingerprint setup page.
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
startActivity(intent);
finish();
} else if (requestCode == SKIP_FINGERPRINT_REQUEST) {

View File

@@ -102,6 +102,7 @@ import java.util.List;
import java.util.Locale;
import static android.content.Intent.EXTRA_USER;
import static android.content.Intent.EXTRA_USER_ID;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
@@ -651,10 +652,15 @@ public final class Utils extends com.android.settingslib.Utils {
/**
* 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>
* User would be retrieved in this order:
* <ul>
* <li> If this activity is launched from other user, return that user id.
* <li> If this is launched from the Settings app in same user, return the user contained as an
* extra in the arguments or intent extras.
* <li> Otherwise, return UserHandle.myUserId().
* </ul>
* <p>
* 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.
@@ -675,16 +681,14 @@ public final class Utils extends com.android.settingslib.Utils {
return launchedFromUser;
}
}
UserHandle extrasUser = intentExtras != null
? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
UserHandle extrasUser = getUserHandleFromBundle(intentExtras);
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;
UserHandle argumentsUser = getUserHandleFromBundle(arguments);
if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
// Check it's secure
if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
@@ -698,6 +702,25 @@ public final class Utils extends com.android.settingslib.Utils {
return currentUser;
}
/**
* Lookup both {@link Intent#EXTRA_USER} and {@link Intent#EXTRA_USER_ID} in the bundle
* and return the {@link UserHandle} object. Return {@code null} if nothing is found.
*/
private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) {
if (bundle == null) {
return null;
}
final UserHandle user = bundle.getParcelable(EXTRA_USER);
if (user != null) {
return user;
}
final int userId = bundle.getInt(EXTRA_USER_ID, -1);
if (userId != -1) {
return UserHandle.of(userId);
}
return null;
}
/**
* Returns the target user for a Settings activity.
*

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.password;
import android.annotation.NonNull;
import android.hardware.fingerprint.FingerprintManager;
import com.android.internal.util.Preconditions;
/**
* Wrapper of {@link FingerprintManager}. Workaround for roboelectic testing. See
* {@link IFingerprintManager} for details.
*/
public class FingerprintManagerWrapper implements IFingerprintManager {
private @NonNull FingerprintManager mFingerprintManager;
public FingerprintManagerWrapper(@NonNull FingerprintManager fingerprintManager) {
Preconditions.checkNotNull(fingerprintManager);
mFingerprintManager = fingerprintManager;
}
public boolean isHardwareDetected() {
return mFingerprintManager.isHardwareDetected();
}
public boolean hasEnrolledFingerprints(int userId) {
return mFingerprintManager.hasEnrolledFingerprints(userId);
}
public long preEnroll() {
return mFingerprintManager.preEnroll();
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.password;
/**
* This is the workaround to allow us test {@link SetNewPasswordController} which uses a new hidden
* API {@link android.hardware.fingerprint.FingerprintManager#hasEnrolledFingerprints(int)} that
* roboelectric does not support yet. Having roboelectic to support latest platform API is tracked
* in b/30995831.
*/
public interface IFingerprintManager {
boolean isHardwareDetected();
boolean hasEnrolledFingerprints(int userId);
long preEnroll();
}

View File

@@ -16,13 +16,18 @@
package com.android.settings.password;
import android.annotation.Nullable;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.android.settings.ChooseLockGeneric;
import com.android.settings.SetupChooseLockGeneric;
import com.android.settings.Utils;
/**
* Trampolines {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} and
@@ -30,6 +35,7 @@ import com.android.settings.ChooseLockGeneric;
* activity for handling set new password.
*/
public class SetNewPasswordActivity extends Activity implements SetNewPasswordController.Ui {
private static final String TAG = "SetNewPasswordActivity";
private String mNewPasswordAction;
private SetNewPasswordController mSetNewPasswordController;
@@ -38,17 +44,22 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
super.onCreate(savedState);
mNewPasswordAction = getIntent().getAction();
mSetNewPasswordController = new SetNewPasswordController(this, this);
if (!ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
&& !ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(mNewPasswordAction)) {
Log.e(TAG, "Unexpected action to launch this activity");
finish();
return;
}
mSetNewPasswordController = SetNewPasswordController.create(
this, this, getIntent(), getActivityToken());
mSetNewPasswordController.dispatchSetNewPasswordIntent();
}
@Override
public void launchChooseLock(@Nullable Bundle chooseLockFingerprintExtras) {
public void launchChooseLock(Bundle chooseLockFingerprintExtras) {
Intent intent = new Intent(this, ChooseLockGeneric.class)
.setAction(mNewPasswordAction);
if (chooseLockFingerprintExtras != null) {
intent.putExtras(chooseLockFingerprintExtras);
}
startActivity(intent);
finish();
}

View File

@@ -16,20 +16,27 @@
package com.android.settings.password;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.IBinder;
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.ChooseLockGeneric;
import com.android.settings.ChooseLockSettingsHelper;
import com.android.settings.Utils;
/**
* Business logic for {@link SetNewPasswordActivity}.
@@ -42,31 +49,54 @@ final class SetNewPasswordController {
interface Ui {
/** Starts the {@link ChooseLockGeneric} activity with the given extras. */
void launchChooseLock(@Nullable Bundle chooseLockFingerprintExtras);
void launchChooseLock(Bundle chooseLockFingerprintExtras);
}
private final int mCurrentUserId;
/**
* Which user is setting new password.
*/
private final int mTargetUserId;
private final PackageManager mPackageManager;
@Nullable private final FingerprintManager mFingerprintManager;
@Nullable private final IFingerprintManager mFingerprintManager;
private final DevicePolicyManager mDevicePolicyManager;
private final Ui mUi;
public SetNewPasswordController(Context context, Ui ui) {
this(context.getUserId(),
public static SetNewPasswordController create(Context context, Ui ui, Intent intent,
IBinder activityToken) {
// Trying to figure out which user is setting new password. If it is
// ACTION_SET_NEW_PARENT_PROFILE_PASSWORD or the calling user is not allowed to set
// separate profile challenge, it is the current user to set new password. Otherwise,
// it is the user who starts this activity setting new password.
int userId = ActivityManager.getCurrentUser();
if (ACTION_SET_NEW_PASSWORD.equals(intent.getAction())) {
final int callingUserId = Utils.getSecureTargetUser(activityToken,
UserManager.get(context), null, intent.getExtras()).getIdentifier();
final LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
if (lockPatternUtils.isSeparateProfileChallengeAllowed(callingUserId)) {
userId = callingUserId;
}
}
// Create a wrapper of FingerprintManager for testing, see IFingerPrintManager for details.
final FingerprintManager fingerprintManager =
(FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
final IFingerprintManager fingerprintManagerWrapper =
fingerprintManager == null
? null
: new FingerprintManagerWrapper(fingerprintManager);
return new SetNewPasswordController(userId,
context.getPackageManager(),
(FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE),
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE),
ui);
fingerprintManagerWrapper,
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE), ui);
}
@VisibleForTesting
SetNewPasswordController(
int currentUserId,
int targetUserId,
PackageManager packageManager,
FingerprintManager fingerprintManager,
IFingerprintManager fingerprintManager,
DevicePolicyManager devicePolicyManager,
Ui ui) {
mCurrentUserId = currentUserId;
mTargetUserId = targetUserId;
mPackageManager = checkNotNull(packageManager);
mFingerprintManager = fingerprintManager;
mDevicePolicyManager = checkNotNull(devicePolicyManager);
@@ -77,38 +107,38 @@ final class SetNewPasswordController {
* Dispatches the set new password intent to the correct activity that handles it.
*/
public void dispatchSetNewPasswordIntent() {
final Bundle extras;
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
&& mFingerprintManager != null
&& mFingerprintManager.isHardwareDetected()
&& !mFingerprintManager.hasEnrolledFingerprints()
&& !mFingerprintManager.hasEnrolledFingerprints(mTargetUserId)
&& !isFingerprintDisabledByAdmin()) {
mUi.launchChooseLock(getFingerprintChooseLockExtras());
extras = getFingerprintChooseLockExtras();
} else {
mUi.launchChooseLock(null);
extras = new Bundle();
}
// No matter we show fingerprint options or not, we should tell the next activity which
// user is setting new password.
extras.putInt(Intent.EXTRA_USER_ID, mTargetUserId);
mUi.launchChooseLock(extras);
}
private Bundle getFingerprintChooseLockExtras() {
Bundle chooseLockExtras = new Bundle();
if (mFingerprintManager != null) {
long challenge = mFingerprintManager.preEnroll();
chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
PASSWORD_QUALITY_SOMETHING);
chooseLockExtras.putBoolean(
ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
chooseLockExtras.putLong(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
if (mCurrentUserId != UserHandle.USER_NULL) {
chooseLockExtras.putInt(Intent.EXTRA_USER_ID, mCurrentUserId);
}
}
return chooseLockExtras;
}
private boolean isFingerprintDisabledByAdmin() {
int disabledFeatures = mDevicePolicyManager.getKeyguardDisabledFeatures(
null, mCurrentUserId);
return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0;
int disabledFeatures =
mDevicePolicyManager.getKeyguardDisabledFeatures(null, mTargetUserId);
return (disabledFeatures & KEYGUARD_DISABLE_FINGERPRINT) != 0;
}
}