Support remote device credentials validation in UI.
Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.password Test: Manual Bug: 258505917 Change-Id: Ifb9f15728eb8396b34c844d28f71a8e6e1aad837
This commit is contained in:
@@ -87,6 +87,7 @@ android_library {
|
|||||||
"fuelgauge-log-protos-lite",
|
"fuelgauge-log-protos-lite",
|
||||||
"fuelgauge-usage-state-protos-lite",
|
"fuelgauge-usage-state-protos-lite",
|
||||||
"contextualcards",
|
"contextualcards",
|
||||||
|
"securebox",
|
||||||
"settings-logtags",
|
"settings-logtags",
|
||||||
"statslog-settings",
|
"statslog-settings",
|
||||||
"zxing-core-1.7",
|
"zxing-core-1.7",
|
||||||
|
@@ -88,6 +88,7 @@
|
|||||||
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
|
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
|
||||||
<uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
|
<uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
|
||||||
<uses-permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE" />
|
<uses-permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.OEM_UNLOCK_STATE" />
|
<uses-permission android:name="android.permission.OEM_UNLOCK_STATE" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
|
<uses-permission android:name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
|
||||||
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
|
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
|
||||||
@@ -2370,6 +2371,17 @@
|
|||||||
<activity-alias android:name=".ConfirmDeviceCredentialActivity"
|
<activity-alias android:name=".ConfirmDeviceCredentialActivity"
|
||||||
android:targetActivity=".password.ConfirmDeviceCredentialActivity"
|
android:targetActivity=".password.ConfirmDeviceCredentialActivity"
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
|
<!-- Activity alias for remote lockscreen validation. Enforces required permission -->
|
||||||
|
<activity-alias
|
||||||
|
android:name=".ConfirmRemoteDeviceCredentialActivity"
|
||||||
|
android:targetActivity=".password.ConfirmDeviceCredentialActivity"
|
||||||
|
android:permission="android.permission.CHECK_REMOTE_LOCKSCREEN"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
<!-- Note this must not be exported since it authenticates the given user -->
|
<!-- Note this must not be exported since it authenticates the given user -->
|
||||||
<activity android:name=".password.ConfirmDeviceCredentialActivity$InternalActivity"
|
<activity android:name=".password.ConfirmDeviceCredentialActivity$InternalActivity"
|
||||||
|
@@ -15,10 +15,12 @@
|
|||||||
-->
|
-->
|
||||||
<com.google.android.setupdesign.GlifLayout
|
<com.google.android.setupdesign.GlifLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/setup_wizard_layout"
|
android:id="@+id/setup_wizard_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:icon="@drawable/ic_lock">
|
android:icon="@drawable/ic_lock"
|
||||||
|
app:sudUseBottomProgressBar="true">
|
||||||
|
|
||||||
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
|
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
|
||||||
android:id="@+id/topLayout"
|
android:id="@+id/topLayout"
|
||||||
@@ -60,6 +62,16 @@
|
|||||||
android:layout_marginEnd="?attr/sudMarginEnd"
|
android:layout_marginEnd="?attr/sudMarginEnd"
|
||||||
android:gravity="center_vertical"/>
|
android:gravity="center_vertical"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="?attr/sudMarginStart"
|
||||||
|
android:layout_marginEnd="?attr/sudMarginEnd"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/cancelButton"
|
android:id="@+id/cancelButton"
|
||||||
style="@style/SudGlifButton.Secondary"
|
style="@style/SudGlifButton.Secondary"
|
||||||
|
@@ -15,11 +15,13 @@
|
|||||||
-->
|
-->
|
||||||
<com.google.android.setupdesign.GlifLayout
|
<com.google.android.setupdesign.GlifLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/setup_wizard_layout"
|
android:id="@+id/setup_wizard_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:icon="@drawable/ic_lock"
|
android:icon="@drawable/ic_lock"
|
||||||
android:importantForAutofill="noExcludeDescendants">
|
android:importantForAutofill="noExcludeDescendants"
|
||||||
|
app:sudUseBottomProgressBar="true">
|
||||||
|
|
||||||
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
|
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
|
||||||
android:id="@+id/topLayout"
|
android:id="@+id/topLayout"
|
||||||
@@ -27,14 +29,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/sud_layout_description"
|
|
||||||
style="@style/SudDescription.Glif"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="?attr/sudMarginStart"
|
|
||||||
android:layout_marginEnd="?attr/sudMarginEnd" />
|
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
@@ -73,6 +67,17 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="?attr/sudMarginStart"
|
||||||
|
android:layout_marginEnd="?attr/sudMarginEnd"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/cancelButton"
|
android:id="@+id/cancelButton"
|
||||||
style="@style/SudGlifButton.Secondary"
|
style="@style/SudGlifButton.Secondary"
|
||||||
|
@@ -15,10 +15,12 @@
|
|||||||
-->
|
-->
|
||||||
<com.google.android.setupdesign.GlifLayout
|
<com.google.android.setupdesign.GlifLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/setup_wizard_layout"
|
android:id="@+id/setup_wizard_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:icon="@drawable/ic_lock">
|
android:icon="@drawable/ic_lock"
|
||||||
|
app:sudUseBottomProgressBar="true">
|
||||||
|
|
||||||
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
|
<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
|
||||||
android:id="@+id/topLayout"
|
android:id="@+id/topLayout"
|
||||||
@@ -61,6 +63,16 @@
|
|||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:gravity="center_vertical"/>
|
android:gravity="center_vertical"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="?attr/sudMarginStart"
|
||||||
|
android:layout_marginEnd="?attr/sudMarginEnd"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/cancelButton"
|
android:id="@+id/cancelButton"
|
||||||
style="@style/SudGlifButton.Secondary"
|
style="@style/SudGlifButton.Secondary"
|
||||||
@@ -68,7 +80,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="?attr/sudMarginStart"
|
android:layout_marginStart="?attr/sudMarginStart"
|
||||||
android:layout_marginEnd="?attr/sudMarginEnd"
|
android:layout_marginEnd="?attr/sudMarginEnd"
|
||||||
android:layout_marginBottom="80dp"
|
|
||||||
android:text="@string/cancel" />
|
android:text="@string/cancel" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@@ -3339,6 +3339,24 @@
|
|||||||
<!-- Header shown when the password needs to be solved because the device was factory reset. [CHAR LIMIT=100] -->
|
<!-- Header shown when the password needs to be solved because the device was factory reset. [CHAR LIMIT=100] -->
|
||||||
<string name="lockpassword_confirm_your_password_header_frp">Verify password</string>
|
<string name="lockpassword_confirm_your_password_header_frp">Verify password</string>
|
||||||
|
|
||||||
|
<!-- Header shown when prompted for remote device credential validation. [CHAR LIMIT=17] -->
|
||||||
|
<string name="lockpassword_remote_validation_header">Verify it\u0027s you</string>
|
||||||
|
|
||||||
|
<!-- Details shown when pattern is prompted for remote device credential validation. [CHAR LIMIT=100] -->
|
||||||
|
<string name="lockpassword_remote_validation_pattern_details">Enter your other device\u0027s pattern to securely transfer Google Accounts, settings, and more. Your pattern is encrypted.</string>
|
||||||
|
<!-- Details shown when PIN is prompted for remote device credential validation. [CHAR LIMIT=100] -->
|
||||||
|
<string name="lockpassword_remote_validation_pin_details">Enter your other device\u0027s PIN to securely transfer Google Accounts, settings, and more. Your PIN is encrypted.</string>
|
||||||
|
<!-- Details shown when password is prompted for remote device credential validation. [CHAR LIMIT=100] -->
|
||||||
|
<string name="lockpassword_remote_validation_password_details">Enter your other device\u0027s password to securely transfer Google Accounts, settings, and more. Your password is encrypted.</string>
|
||||||
|
|
||||||
|
<!-- Checkbox label to set pattern as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
|
||||||
|
<string name="lockpassword_remote_validation_set_pattern_as_screenlock">Also use pattern to unlock this device</string>
|
||||||
|
<!-- Checkbox label to set PIN as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
|
||||||
|
<string name="lockpassword_remote_validation_set_pin_as_screenlock">Also use PIN to unlock this device</string>
|
||||||
|
<!-- Checkbox label to set password as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
|
||||||
|
<string name="lockpassword_remote_validation_set_password_as_screenlock">Also use password to unlock this device</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Security & location settings screen, change security method screen instruction if user
|
<!-- Security & location settings screen, change security method screen instruction if user
|
||||||
enters incorrect PIN [CHAR LIMIT=30] -->
|
enters incorrect PIN [CHAR LIMIT=30] -->
|
||||||
<string name="lockpassword_invalid_pin">Wrong PIN</string>
|
<string name="lockpassword_invalid_pin">Wrong PIN</string>
|
||||||
|
@@ -22,7 +22,9 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.StartLockscreenValidationRequest;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
@@ -41,6 +43,8 @@ import com.android.settingslib.transition.SettingsTransitionHelper;
|
|||||||
|
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class ChooseLockSettingsHelper {
|
public final class ChooseLockSettingsHelper {
|
||||||
|
|
||||||
private static final String TAG = "ChooseLockSettingsHelper";
|
private static final String TAG = "ChooseLockSettingsHelper";
|
||||||
@@ -132,6 +136,7 @@ public final class ChooseLockSettingsHelper {
|
|||||||
@Nullable private CharSequence mHeader;
|
@Nullable private CharSequence mHeader;
|
||||||
@Nullable private CharSequence mDescription;
|
@Nullable private CharSequence mDescription;
|
||||||
@Nullable private CharSequence mAlternateButton;
|
@Nullable private CharSequence mAlternateButton;
|
||||||
|
@Nullable private CharSequence mCheckBoxLabel;
|
||||||
private boolean mReturnCredentials;
|
private boolean mReturnCredentials;
|
||||||
private boolean mExternal;
|
private boolean mExternal;
|
||||||
private boolean mForegroundOnly;
|
private boolean mForegroundOnly;
|
||||||
@@ -139,6 +144,9 @@ public final class ChooseLockSettingsHelper {
|
|||||||
private int mUserId;
|
private int mUserId;
|
||||||
private boolean mAllowAnyUserId;
|
private boolean mAllowAnyUserId;
|
||||||
private boolean mForceVerifyPath;
|
private boolean mForceVerifyPath;
|
||||||
|
private boolean mRemoteLockscreenValidation;
|
||||||
|
@Nullable private StartLockscreenValidationRequest mStartLockscreenValidationRequest;
|
||||||
|
@Nullable private ComponentName mRemoteLockscreenValidationServiceComponent;
|
||||||
boolean mRequestGatekeeperPasswordHandle;
|
boolean mRequestGatekeeperPasswordHandle;
|
||||||
|
|
||||||
public Builder(@NonNull Activity activity) {
|
public Builder(@NonNull Activity activity) {
|
||||||
@@ -191,6 +199,15 @@ public final class ChooseLockSettingsHelper {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param checkboxLabel text for the checkbox
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder setCheckboxLabel(@Nullable CharSequence checkboxLabel) {
|
||||||
|
mCheckBoxLabel = checkboxLabel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param returnCredentials if true, puts the following credentials into intent for
|
* @param returnCredentials if true, puts the following credentials into intent for
|
||||||
* onActivityResult with the following keys:
|
* onActivityResult with the following keys:
|
||||||
@@ -253,6 +270,42 @@ public final class ChooseLockSettingsHelper {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param isRemoteLockscreenValidation if true, remote device validation flow will be
|
||||||
|
* started. {@link #setStartLockscreenValidationRequest} and
|
||||||
|
* {@link #setRemoteLockscreenValidationServiceComponent}
|
||||||
|
* must also be used to set the required data.
|
||||||
|
*/
|
||||||
|
@NonNull public Builder setRemoteLockscreenValidation(
|
||||||
|
boolean isRemoteLockscreenValidation) {
|
||||||
|
mRemoteLockscreenValidation = isRemoteLockscreenValidation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param startLockScreenValidationRequest contains information necessary to perform remote
|
||||||
|
* lockscreen validation such as the remote device's
|
||||||
|
* lockscreen type, public key to be used for
|
||||||
|
* encryption, and remaining attempts.
|
||||||
|
*/
|
||||||
|
@NonNull public Builder setStartLockscreenValidationRequest(
|
||||||
|
StartLockscreenValidationRequest startLockScreenValidationRequest) {
|
||||||
|
mStartLockscreenValidationRequest = startLockScreenValidationRequest;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param remoteLockscreenValidationServiceComponent the {@link ComponentName} of the
|
||||||
|
* {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
|
||||||
|
* that will be used to validate the lockscreen guess.
|
||||||
|
*/
|
||||||
|
@NonNull public Builder setRemoteLockscreenValidationServiceComponent(
|
||||||
|
ComponentName remoteLockscreenValidationServiceComponent) {
|
||||||
|
mRemoteLockscreenValidationServiceComponent =
|
||||||
|
remoteLockscreenValidationServiceComponent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that LockSettingsService return a handle to the Gatekeeper Password (instead of
|
* Requests that LockSettingsService return a handle to the Gatekeeper Password (instead of
|
||||||
* the Gatekeeper HAT). This allows us to use a single entry of the user's credential
|
* the Gatekeeper HAT). This allows us to use a single entry of the user's credential
|
||||||
@@ -315,49 +368,41 @@ public final class ChooseLockSettingsHelper {
|
|||||||
return launchConfirmationActivity(mBuilder.mRequestCode, mBuilder.mTitle, mBuilder.mHeader,
|
return launchConfirmationActivity(mBuilder.mRequestCode, mBuilder.mTitle, mBuilder.mHeader,
|
||||||
mBuilder.mDescription, mBuilder.mReturnCredentials, mBuilder.mExternal,
|
mBuilder.mDescription, mBuilder.mReturnCredentials, mBuilder.mExternal,
|
||||||
mBuilder.mForceVerifyPath, mBuilder.mUserId, mBuilder.mAlternateButton,
|
mBuilder.mForceVerifyPath, mBuilder.mUserId, mBuilder.mAlternateButton,
|
||||||
mBuilder.mAllowAnyUserId, mBuilder.mForegroundOnly,
|
mBuilder.mCheckBoxLabel, mBuilder.mRemoteLockscreenValidation,
|
||||||
mBuilder.mRequestGatekeeperPasswordHandle);
|
mBuilder.mStartLockscreenValidationRequest,
|
||||||
|
mBuilder.mRemoteLockscreenValidationServiceComponent, mBuilder.mAllowAnyUserId,
|
||||||
|
mBuilder.mForegroundOnly, mBuilder.mRequestGatekeeperPasswordHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
||||||
@Nullable CharSequence header, @Nullable CharSequence description,
|
@Nullable CharSequence header, @Nullable CharSequence description,
|
||||||
boolean returnCredentials, boolean external, boolean forceVerifyPath,
|
boolean returnCredentials, boolean external, boolean forceVerifyPath,
|
||||||
int userId, @Nullable CharSequence alternateButton, boolean allowAnyUser,
|
int userId, @Nullable CharSequence alternateButton,
|
||||||
boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
|
@Nullable CharSequence checkboxLabel, boolean remoteLockscreenValidation,
|
||||||
final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId);
|
@Nullable StartLockscreenValidationRequest startLockScreenValidationRequest,
|
||||||
boolean launched = false;
|
@Nullable ComponentName remoteLockscreenValidationServiceComponent,
|
||||||
|
boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
|
||||||
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) {
|
Optional<Class<?>> activityClass = determineAppropriateActivityClass(
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
returnCredentials, forceVerifyPath, userId, startLockScreenValidationRequest);
|
||||||
launched = launchConfirmationActivity(request, title, header, description,
|
if (activityClass.isEmpty()) {
|
||||||
returnCredentials || forceVerifyPath
|
return false;
|
||||||
? ConfirmLockPattern.InternalActivity.class
|
|
||||||
: ConfirmLockPattern.class, returnCredentials, external,
|
|
||||||
forceVerifyPath, userId, alternateButton, allowAnyUser,
|
|
||||||
foregroundOnly, requestGatekeeperPasswordHandle);
|
|
||||||
break;
|
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
|
||||||
launched = launchConfirmationActivity(request, title, header, description,
|
|
||||||
returnCredentials || forceVerifyPath
|
|
||||||
? ConfirmLockPassword.InternalActivity.class
|
|
||||||
: ConfirmLockPassword.class, returnCredentials, external,
|
|
||||||
forceVerifyPath, userId, alternateButton, allowAnyUser,
|
|
||||||
foregroundOnly, requestGatekeeperPasswordHandle);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return launched;
|
|
||||||
|
return launchConfirmationActivity(request, title, header, description, activityClass.get(),
|
||||||
|
returnCredentials, external, forceVerifyPath, userId, alternateButton,
|
||||||
|
checkboxLabel, remoteLockscreenValidation, startLockScreenValidationRequest,
|
||||||
|
remoteLockscreenValidationServiceComponent, allowAnyUser, foregroundOnly,
|
||||||
|
requestGatekeeperPasswordHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
|
private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
|
||||||
CharSequence message, Class<?> activityClass, boolean returnCredentials,
|
CharSequence message, Class<?> activityClass, boolean returnCredentials,
|
||||||
boolean external, boolean forceVerifyPath, int userId,
|
boolean external, boolean forceVerifyPath, int userId,
|
||||||
@Nullable CharSequence alternateButton, boolean allowAnyUser,
|
@Nullable CharSequence alternateButton, @Nullable CharSequence checkbox,
|
||||||
boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
|
boolean remoteLockscreenValidation,
|
||||||
|
@Nullable StartLockscreenValidationRequest startLockScreenValidationRequest,
|
||||||
|
@Nullable ComponentName remoteLockscreenValidationServiceComponent,
|
||||||
|
boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
|
||||||
final Intent intent = new Intent();
|
final Intent intent = new Intent();
|
||||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
|
intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
|
||||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
|
intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
|
||||||
@@ -367,10 +412,16 @@ public final class ChooseLockSettingsHelper {
|
|||||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, false);
|
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, false);
|
||||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
|
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
|
||||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.USE_FADE_ANIMATION, external);
|
intent.putExtra(ConfirmDeviceCredentialBaseFragment.USE_FADE_ANIMATION, external);
|
||||||
|
intent.putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION,
|
||||||
|
remoteLockscreenValidation);
|
||||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
|
||||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, forceVerifyPath);
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, forceVerifyPath);
|
||||||
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||||
intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton);
|
intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton);
|
||||||
|
intent.putExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL, checkbox);
|
||||||
|
intent.putExtra(KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
startLockScreenValidationRequest);
|
||||||
|
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent);
|
||||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, foregroundOnly);
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, foregroundOnly);
|
||||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser);
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser);
|
||||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE,
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE,
|
||||||
@@ -405,6 +456,58 @@ public final class ChooseLockSettingsHelper {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<Integer> passwordQualityToLockTypes(int quality) {
|
||||||
|
switch (quality) {
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
||||||
|
return Optional.of(KeyguardManager.PATTERN);
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
||||||
|
return Optional.of(KeyguardManager.PIN);
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
||||||
|
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
||||||
|
return Optional.of(KeyguardManager.PASSWORD);
|
||||||
|
}
|
||||||
|
Log.e(TAG, String.format(
|
||||||
|
"Cannot determine appropriate activity class for password quality %d",
|
||||||
|
quality));
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Class<?>> determineAppropriateActivityClass(boolean returnCredentials,
|
||||||
|
boolean forceVerifyPath, int userId,
|
||||||
|
@Nullable StartLockscreenValidationRequest startLockscreenValidationRequest) {
|
||||||
|
int lockType;
|
||||||
|
if (startLockscreenValidationRequest != null) {
|
||||||
|
lockType = startLockscreenValidationRequest.getLockscreenUiType();
|
||||||
|
} else {
|
||||||
|
final int effectiveUserId = UserManager
|
||||||
|
.get(mActivity).getCredentialOwnerProfile(userId);
|
||||||
|
Optional<Integer> lockTypeOptional = passwordQualityToLockTypes(
|
||||||
|
mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId));
|
||||||
|
if (lockTypeOptional.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
lockType = lockTypeOptional.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lockType) {
|
||||||
|
case KeyguardManager.PASSWORD:
|
||||||
|
case KeyguardManager.PIN:
|
||||||
|
return Optional.of(returnCredentials || forceVerifyPath
|
||||||
|
? ConfirmLockPassword.InternalActivity.class
|
||||||
|
: ConfirmLockPassword.class);
|
||||||
|
case KeyguardManager.PATTERN:
|
||||||
|
return Optional.of(returnCredentials || forceVerifyPath
|
||||||
|
? ConfirmLockPattern.InternalActivity.class
|
||||||
|
: ConfirmLockPattern.class);
|
||||||
|
}
|
||||||
|
Log.e(TAG, String.format("Cannot determine appropriate activity class for lock type %d",
|
||||||
|
lockType));
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private void copyOptionalExtras(Intent inIntent, Intent outIntent) {
|
private void copyOptionalExtras(Intent inIntent, Intent outIntent) {
|
||||||
IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
|
IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||||
if (intentSender != null) {
|
if (intentSender != null) {
|
||||||
|
@@ -28,8 +28,10 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.StartLockscreenValidationRequest;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.trust.TrustManager;
|
import android.app.trust.TrustManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
@@ -97,7 +99,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
|||||||
private boolean mCheckDevicePolicyManager;
|
private boolean mCheckDevicePolicyManager;
|
||||||
|
|
||||||
private String mTitle;
|
private String mTitle;
|
||||||
private String mDetails;
|
private CharSequence mDetails;
|
||||||
private int mUserId;
|
private int mUserId;
|
||||||
private int mCredentialMode;
|
private int mCredentialMode;
|
||||||
private boolean mGoingToBackground;
|
private boolean mGoingToBackground;
|
||||||
@@ -178,10 +180,12 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
|||||||
mCheckDevicePolicyManager = intent
|
mCheckDevicePolicyManager = intent
|
||||||
.getBooleanExtra(KeyguardManager.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
|
.getBooleanExtra(KeyguardManager.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
|
||||||
mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
|
mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
|
||||||
mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
|
mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION);
|
||||||
String alternateButton = intent.getStringExtra(
|
String alternateButton = intent.getStringExtra(
|
||||||
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
|
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
|
||||||
boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
|
boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
|
||||||
|
boolean remoteValidation =
|
||||||
|
KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction());
|
||||||
|
|
||||||
mUserId = UserHandle.myUserId();
|
mUserId = UserHandle.myUserId();
|
||||||
if (isInternalActivity()) {
|
if (isInternalActivity()) {
|
||||||
@@ -230,6 +234,28 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
|||||||
.setExternal(true)
|
.setExternal(true)
|
||||||
.setUserId(LockPatternUtils.USER_FRP)
|
.setUserId(LockPatternUtils.USER_FRP)
|
||||||
.show();
|
.show();
|
||||||
|
} else if (remoteValidation) {
|
||||||
|
StartLockscreenValidationRequest startLockScreenValidationRequest =
|
||||||
|
intent.getParcelableExtra(
|
||||||
|
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
StartLockscreenValidationRequest.class);
|
||||||
|
ComponentName remoteLockscreenValidationServiceComponent =
|
||||||
|
intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
|
||||||
|
|
||||||
|
String checkboxLabel = intent.getStringExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
|
||||||
|
final ChooseLockSettingsHelper.Builder builder =
|
||||||
|
new ChooseLockSettingsHelper.Builder(this);
|
||||||
|
launchedCDC = builder
|
||||||
|
.setRemoteLockscreenValidation(true)
|
||||||
|
.setStartLockscreenValidationRequest(startLockScreenValidationRequest)
|
||||||
|
.setRemoteLockscreenValidationServiceComponent(
|
||||||
|
remoteLockscreenValidationServiceComponent)
|
||||||
|
.setHeader(mTitle) // Show the title in the header location
|
||||||
|
.setDescription(mDetails)
|
||||||
|
.setCheckboxLabel(checkboxLabel)
|
||||||
|
.setAlternateButton(alternateButton)
|
||||||
|
.setExternal(true)
|
||||||
|
.show();
|
||||||
} else if (isEffectiveUserManagedProfile && isInternalActivity()) {
|
} else if (isEffectiveUserManagedProfile && isInternalActivity()) {
|
||||||
mCredentialMode = CREDENTIAL_MANAGED;
|
mCredentialMode = CREDENTIAL_MANAGED;
|
||||||
if (isBiometricAllowed(effectiveUserId, mUserId)) {
|
if (isBiometricAllowed(effectiveUserId, mUserId)) {
|
||||||
|
@@ -24,8 +24,11 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.RemoteLockscreenValidationResult;
|
||||||
|
import android.app.StartLockscreenValidationRequest;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.admin.ManagedSubscriptionsPolicy;
|
import android.app.admin.ManagedSubscriptionsPolicy;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -35,11 +38,15 @@ import android.os.Bundle;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
|
||||||
|
import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
|
||||||
import android.telecom.TelecomManager;
|
import android.telecom.TelecomManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.FeatureFlagUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
@@ -47,10 +54,16 @@ import androidx.fragment.app.DialogFragment;
|
|||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.internal.widget.LockscreenCredential;
|
||||||
|
import com.android.security.SecureBox;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.core.InstrumentedFragment;
|
import com.android.settings.core.InstrumentedFragment;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
|
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
|
||||||
*/
|
*/
|
||||||
@@ -66,6 +79,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.showWhenLocked";
|
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.showWhenLocked";
|
||||||
public static final String USE_FADE_ANIMATION =
|
public static final String USE_FADE_ANIMATION =
|
||||||
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.useFadeAnimation";
|
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.useFadeAnimation";
|
||||||
|
public static final String IS_REMOTE_LOCKSCREEN_VALIDATION =
|
||||||
|
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.isRemoteLockscreenValidation";
|
||||||
|
|
||||||
protected static final int USER_TYPE_PRIMARY = 1;
|
protected static final int USER_TYPE_PRIMARY = 1;
|
||||||
protected static final int USER_TYPE_MANAGED_PROFILE = 2;
|
protected static final int USER_TYPE_MANAGED_PROFILE = 2;
|
||||||
@@ -77,6 +92,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
protected boolean mReturnCredentials = false;
|
protected boolean mReturnCredentials = false;
|
||||||
protected boolean mReturnGatekeeperPassword = false;
|
protected boolean mReturnGatekeeperPassword = false;
|
||||||
protected boolean mForceVerifyPath = false;
|
protected boolean mForceVerifyPath = false;
|
||||||
|
protected CheckBox mCheckBox;
|
||||||
protected Button mCancelButton;
|
protected Button mCancelButton;
|
||||||
/** Button allowing managed profile password reset, null when is not shown. */
|
/** Button allowing managed profile password reset, null when is not shown. */
|
||||||
@Nullable protected Button mForgotButton;
|
@Nullable protected Button mForgotButton;
|
||||||
@@ -88,8 +104,13 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
protected TextView mErrorTextView;
|
protected TextView mErrorTextView;
|
||||||
protected final Handler mHandler = new Handler();
|
protected final Handler mHandler = new Handler();
|
||||||
protected boolean mFrp;
|
protected boolean mFrp;
|
||||||
private CharSequence mFrpAlternateButtonText;
|
protected boolean mRemoteValidation;
|
||||||
|
protected CharSequence mAlternateButtonText;
|
||||||
protected BiometricManager mBiometricManager;
|
protected BiometricManager mBiometricManager;
|
||||||
|
@Nullable protected StartLockscreenValidationRequest mStartLockscreenValidationRequest;
|
||||||
|
/** Credential saved so the credential can be set for device if remote validation passes */
|
||||||
|
@Nullable protected LockscreenCredential mDeviceCredentialGuess;
|
||||||
|
@Nullable protected RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
private boolean isInternalActivity() {
|
private boolean isInternalActivity() {
|
||||||
return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
|
return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
|
||||||
@@ -100,7 +121,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
final Intent intent = getActivity().getIntent();
|
final Intent intent = getActivity().getIntent();
|
||||||
mFrpAlternateButtonText = intent.getCharSequenceExtra(
|
mAlternateButtonText = intent.getCharSequenceExtra(
|
||||||
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
|
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
|
||||||
mReturnCredentials = intent.getBooleanExtra(
|
mReturnCredentials = intent.getBooleanExtra(
|
||||||
ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, false);
|
ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, false);
|
||||||
@@ -110,6 +131,41 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
mForceVerifyPath = intent.getBooleanExtra(
|
mForceVerifyPath = intent.getBooleanExtra(
|
||||||
ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, false);
|
ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, false);
|
||||||
|
|
||||||
|
if (intent.getBooleanExtra(IS_REMOTE_LOCKSCREEN_VALIDATION, false)) {
|
||||||
|
if (FeatureFlagUtils.isEnabled(getContext(),
|
||||||
|
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION)) {
|
||||||
|
mRemoteValidation = true;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Remote device credential validation not enabled.");
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
mStartLockscreenValidationRequest = intent.getParcelableExtra(
|
||||||
|
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
StartLockscreenValidationRequest.class);
|
||||||
|
if (mStartLockscreenValidationRequest == null
|
||||||
|
|| mStartLockscreenValidationRequest.getRemainingAttempts() == 0) {
|
||||||
|
Log.e(TAG, "StartLockscreenValidationRequest is null or "
|
||||||
|
+ "no more attempts for remote lockscreen validation.");
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentName remoteLockscreenValidationServiceComponent =
|
||||||
|
intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
|
||||||
|
if (remoteLockscreenValidationServiceComponent == null) {
|
||||||
|
Log.e(TAG, "RemoteLockscreenValidationService ComponentName is null");
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
mRemoteLockscreenValidationClient = RemoteLockscreenValidationClient
|
||||||
|
.create(getContext(), remoteLockscreenValidationServiceComponent);
|
||||||
|
if (!mRemoteLockscreenValidationClient.isServiceAvailable()) {
|
||||||
|
Log.e(TAG, String.format("RemoteLockscreenValidationService at %s is not available",
|
||||||
|
remoteLockscreenValidationServiceComponent.getClassName()));
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only take this argument into account if it belongs to the current profile.
|
// Only take this argument into account if it belongs to the current profile.
|
||||||
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(),
|
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(),
|
||||||
isInternalActivity());
|
isInternalActivity());
|
||||||
@@ -126,13 +182,14 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
mCancelButton = view.findViewById(R.id.cancelButton);
|
mCancelButton = view.findViewById(R.id.cancelButton);
|
||||||
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
|
boolean showCancelButton = mRemoteValidation || getActivity().getIntent().getBooleanExtra(
|
||||||
SHOW_CANCEL_BUTTON, false);
|
SHOW_CANCEL_BUTTON, false);
|
||||||
boolean hasAlternateButton = mFrp && !TextUtils.isEmpty(mFrpAlternateButtonText);
|
boolean hasAlternateButton = (mFrp || mRemoteValidation) && !TextUtils.isEmpty(
|
||||||
|
mAlternateButtonText);
|
||||||
mCancelButton.setVisibility(showCancelButton || hasAlternateButton
|
mCancelButton.setVisibility(showCancelButton || hasAlternateButton
|
||||||
? View.VISIBLE : View.GONE);
|
? View.VISIBLE : View.GONE);
|
||||||
if (hasAlternateButton) {
|
if (hasAlternateButton) {
|
||||||
mCancelButton.setText(mFrpAlternateButtonText);
|
mCancelButton.setText(mAlternateButtonText);
|
||||||
}
|
}
|
||||||
mCancelButton.setOnClickListener(v -> {
|
mCancelButton.setOnClickListener(v -> {
|
||||||
if (hasAlternateButton) {
|
if (hasAlternateButton) {
|
||||||
@@ -141,6 +198,11 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
});
|
});
|
||||||
setupForgotButtonIfManagedProfile(view);
|
setupForgotButtonIfManagedProfile(view);
|
||||||
|
|
||||||
|
mCheckBox = view.findViewById(R.id.checkbox);
|
||||||
|
if (mCheckBox != null && mRemoteValidation) {
|
||||||
|
mCheckBox.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
setupEmergencyCallButtonIfManagedSubscription(view);
|
setupEmergencyCallButtonIfManagedSubscription(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,8 +294,21 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
if (mRemoteLockscreenValidationClient != null) {
|
||||||
|
mRemoteLockscreenValidationClient.disconnect();
|
||||||
|
}
|
||||||
|
if (mDeviceCredentialGuess != null) {
|
||||||
|
mDeviceCredentialGuess.zeroize();
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void authenticationSucceeded();
|
protected abstract void authenticationSucceeded();
|
||||||
|
|
||||||
|
protected abstract void onRemoteDeviceCredentialValidationResult(
|
||||||
|
RemoteLockscreenValidationResult result);
|
||||||
|
|
||||||
public void prepareEnterAnimation() {
|
public void prepareEnterAnimation() {
|
||||||
}
|
}
|
||||||
@@ -335,6 +410,46 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void validateGuess(LockscreenCredential credentialGuess) {
|
||||||
|
if (mCheckBox.isChecked()) {
|
||||||
|
// Keep credential in memory since user wants to set guess as screen lock.
|
||||||
|
mDeviceCredentialGuess = credentialGuess;
|
||||||
|
} else if (mDeviceCredentialGuess != null) {
|
||||||
|
mDeviceCredentialGuess.zeroize();
|
||||||
|
}
|
||||||
|
|
||||||
|
mRemoteLockscreenValidationClient.validateLockscreenGuess(
|
||||||
|
encryptDeviceCredentialGuess(credentialGuess.getCredential()),
|
||||||
|
new IRemoteLockscreenValidationCallback.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(RemoteLockscreenValidationResult result) {
|
||||||
|
mHandler.post(()->onRemoteDeviceCredentialValidationResult(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String message) {
|
||||||
|
Log.e(TAG, "A failure occurred while trying "
|
||||||
|
+ "to validate lockscreen guess: " + message);
|
||||||
|
mHandler.post(()->getActivity().finish());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] encryptDeviceCredentialGuess(byte[] guess) {
|
||||||
|
try {
|
||||||
|
byte[] encodedPublicKey = mStartLockscreenValidationRequest.getSourcePublicKey();
|
||||||
|
PublicKey publicKey = SecureBox.decodePublicKey(encodedPublicKey);
|
||||||
|
return SecureBox.encrypt(
|
||||||
|
publicKey,
|
||||||
|
/* sharedSecret= */ null,
|
||||||
|
LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER,
|
||||||
|
guess);
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
|
Log.w(TAG, "Error encrypting device credential guess. Returning empty byte[].", e);
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void onShowError();
|
protected abstract void onShowError();
|
||||||
|
|
||||||
protected void showError(int msg, long timeout) {
|
protected void showError(int msg, long timeout) {
|
||||||
|
@@ -27,6 +27,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROF
|
|||||||
import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.RemoteLockscreenValidationResult;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -42,6 +44,7 @@ import android.os.UserManager;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -120,7 +123,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
|
|
||||||
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
|
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
|
||||||
implements OnClickListener, OnEditorActionListener,
|
implements OnClickListener, OnEditorActionListener,
|
||||||
CredentialCheckResultTracker.Listener {
|
CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener {
|
||||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||||
private ImeAwareEditText mPasswordEntry;
|
private ImeAwareEditText mPasswordEntry;
|
||||||
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
||||||
@@ -134,6 +137,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||||
private boolean mIsManagedProfile;
|
private boolean mIsManagedProfile;
|
||||||
private GlifLayout mGlifLayout;
|
private GlifLayout mGlifLayout;
|
||||||
|
private CharSequence mCheckBoxLabel;
|
||||||
|
|
||||||
// required constructor for fragments
|
// required constructor for fragments
|
||||||
public ConfirmLockPasswordFragment() {
|
public ConfirmLockPasswordFragment() {
|
||||||
@@ -160,11 +164,19 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
mPasswordEntry.requestFocus();
|
mPasswordEntry.requestFocus();
|
||||||
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
|
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
|
||||||
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
|
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
|
||||||
|
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
mIsAlpha = mStartLockscreenValidationRequest.getLockscreenUiType()
|
||||||
|
== KeyguardManager.PASSWORD;
|
||||||
|
// ProgressBar visibility is set to GONE until interacted with.
|
||||||
|
// Set progress bar to INVISIBLE, so the EditText does not get bumped down later.
|
||||||
|
mGlifLayout.setProgressBarShown(false);
|
||||||
|
} else {
|
||||||
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|
||||||
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|
||||||
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|
||||||
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
|
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
|
||||||
|
}
|
||||||
mImm = (InputMethodManager) getActivity().getSystemService(
|
mImm = (InputMethodManager) getActivity().getSystemService(
|
||||||
Context.INPUT_METHOD_SERVICE);
|
Context.INPUT_METHOD_SERVICE);
|
||||||
|
|
||||||
@@ -187,6 +199,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
}
|
}
|
||||||
mGlifLayout.setHeaderText(headerMessage);
|
mGlifLayout.setHeaderText(headerMessage);
|
||||||
mGlifLayout.setDescriptionText(detailsMessage);
|
mGlifLayout.setDescriptionText(detailsMessage);
|
||||||
|
mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
|
||||||
}
|
}
|
||||||
int currentType = mPasswordEntry.getInputType();
|
int currentType = mPasswordEntry.getInputType();
|
||||||
if (mIsAlpha) {
|
if (mIsAlpha) {
|
||||||
@@ -227,6 +240,19 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
if (mCheckBox != null) {
|
||||||
|
mCheckBox.setText(TextUtils.isEmpty(mCheckBoxLabel)
|
||||||
|
? getDefaultCheckboxLabel()
|
||||||
|
: mCheckBoxLabel);
|
||||||
|
}
|
||||||
|
if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
|
||||||
|
mCancelButton.setText(mIsAlpha
|
||||||
|
? R.string.lockpassword_forgot_password
|
||||||
|
: R.string.lockpassword_forgot_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mForgotButton != null) {
|
if (mForgotButton != null) {
|
||||||
mForgotButton.setText(mIsAlpha
|
mForgotButton.setText(mIsAlpha
|
||||||
? R.string.lockpassword_forgot_password
|
? R.string.lockpassword_forgot_password
|
||||||
@@ -237,7 +263,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
if (mPasswordEntry != null) {
|
||||||
mPasswordEntry.setText(null);
|
mPasswordEntry.setText(null);
|
||||||
|
}
|
||||||
// Force a garbage collection to remove remnant of user password shards from memory.
|
// Force a garbage collection to remove remnant of user password shards from memory.
|
||||||
// Execute this with a slight delay to allow the activity lifecycle to complete and
|
// Execute this with a slight delay to allow the activity lifecycle to complete and
|
||||||
// the instance to become gc-able.
|
// the instance to become gc-able.
|
||||||
@@ -253,6 +281,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp)
|
return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp)
|
||||||
: getString(R.string.lockpassword_confirm_your_pin_header_frp);
|
: getString(R.string.lockpassword_confirm_your_pin_header_frp);
|
||||||
}
|
}
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
return getString(R.string.lockpassword_remote_validation_header);
|
||||||
|
}
|
||||||
if (mIsManagedProfile) {
|
if (mIsManagedProfile) {
|
||||||
if (mIsAlpha) {
|
if (mIsAlpha) {
|
||||||
return mDevicePolicyManager.getResources().getString(
|
return mDevicePolicyManager.getResources().getString(
|
||||||
@@ -273,6 +304,11 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp)
|
return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp)
|
||||||
: getString(R.string.lockpassword_confirm_your_pin_details_frp);
|
: getString(R.string.lockpassword_confirm_your_pin_details_frp);
|
||||||
}
|
}
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
return getContext().getString(mIsAlpha
|
||||||
|
? R.string.lockpassword_remote_validation_password_details
|
||||||
|
: R.string.lockpassword_remote_validation_pin_details);
|
||||||
|
}
|
||||||
boolean isStrongAuthRequired = isStrongAuthRequired();
|
boolean isStrongAuthRequired = isStrongAuthRequired();
|
||||||
// Map boolean flags to an index by isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
|
// Map boolean flags to an index by isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
|
||||||
int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((mIsManagedProfile ? 1 : 0) << 1)
|
int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((mIsManagedProfile ? 1 : 0) << 1)
|
||||||
@@ -281,6 +317,16 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
DETAIL_TEXT_OVERRIDES[index], () -> getString(DETAIL_TEXTS[index]));
|
DETAIL_TEXT_OVERRIDES[index], () -> getString(DETAIL_TEXTS[index]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getDefaultCheckboxLabel() {
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
return getString(mIsAlpha
|
||||||
|
? R.string.lockpassword_remote_validation_set_password_as_screenlock
|
||||||
|
: R.string.lockpassword_remote_validation_set_pin_as_screenlock);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Trying to get default checkbox label for illegal flow");
|
||||||
|
}
|
||||||
|
|
||||||
private int getErrorMessage() {
|
private int getErrorMessage() {
|
||||||
return mIsAlpha ? R.string.lockpassword_invalid_password
|
return mIsAlpha ? R.string.lockpassword_invalid_password
|
||||||
: R.string.lockpassword_invalid_pin;
|
: R.string.lockpassword_invalid_pin;
|
||||||
@@ -392,6 +438,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
|
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
|
||||||
} else {
|
} else {
|
||||||
mPasswordEntry.scheduleShowSoftInput();
|
mPasswordEntry.scheduleShowSoftInput();
|
||||||
|
mPasswordEntry.requestFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,12 +460,18 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
if (TextUtils.isEmpty(passwordText)) {
|
if (TextUtils.isEmpty(passwordText)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final LockscreenCredential credential =
|
final LockscreenCredential credential = mIsAlpha
|
||||||
mIsAlpha ? LockscreenCredential.createPassword(passwordText)
|
? LockscreenCredential.createPassword(passwordText)
|
||||||
: LockscreenCredential.createPin(passwordText);
|
: LockscreenCredential.createPin(passwordText);
|
||||||
|
|
||||||
mPasswordEntryInputDisabler.setInputEnabled(false);
|
mPasswordEntryInputDisabler.setInputEnabled(false);
|
||||||
|
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
validateGuess(credential);
|
||||||
|
mGlifLayout.setProgressBarShown(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
// TODO(b/161956762): Sanitize this
|
// TODO(b/161956762): Sanitize this
|
||||||
if (mReturnGatekeeperPassword) {
|
if (mReturnGatekeeperPassword) {
|
||||||
@@ -546,6 +599,44 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemoteDeviceCredentialValidationResult(
|
||||||
|
RemoteLockscreenValidationResult result) {
|
||||||
|
switch (result.getResultCode()) {
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
|
||||||
|
if (mCheckBox.isChecked()) {
|
||||||
|
ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker =
|
||||||
|
new ChooseLockPassword.SaveAndFinishWorker();
|
||||||
|
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
|
||||||
|
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
|
||||||
|
.commit();
|
||||||
|
getFragmentManager().executePendingTransactions();
|
||||||
|
saveAndFinishWorker.setListener(this);
|
||||||
|
saveAndFinishWorker.start(
|
||||||
|
mLockPatternUtils,
|
||||||
|
/* requestGatekeeperPassword= */ false,
|
||||||
|
mDeviceCredentialGuess,
|
||||||
|
/* currentCredential= */ null,
|
||||||
|
mEffectiveUserId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||||
|
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||||
|
break;
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
|
||||||
|
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||||
|
break;
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_LOCKOUT:
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
|
||||||
|
(int) result.getTimeoutMillis(), mEffectiveUserId);
|
||||||
|
break;
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
mGlifLayout.setProgressBarShown(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||||
int effectiveUserId, boolean newResult) {
|
int effectiveUserId, boolean newResult) {
|
||||||
@@ -601,5 +692,19 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for when the device credential guess used for remote validation was set as the
|
||||||
|
* current device's device credential.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||||
|
if (mDeviceCredentialGuess != null) {
|
||||||
|
mDeviceCredentialGuess.zeroize();
|
||||||
|
}
|
||||||
|
mGlifLayout.setProgressBarShown(false);
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||||
|
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.RemoteLockscreenValidationResult;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
@@ -33,6 +35,7 @@ import android.os.CountDownTimer;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -89,7 +92,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
|
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
|
||||||
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener {
|
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
|
||||||
|
SaveChosenLockWorkerBase.Listener {
|
||||||
|
|
||||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||||
|
|
||||||
@@ -105,6 +109,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
// caller-supplied text for various prompts
|
// caller-supplied text for various prompts
|
||||||
private CharSequence mHeaderText;
|
private CharSequence mHeaderText;
|
||||||
private CharSequence mDetailsText;
|
private CharSequence mDetailsText;
|
||||||
|
private CharSequence mCheckBoxLabel;
|
||||||
|
|
||||||
private AppearAnimationUtils mAppearAnimationUtils;
|
private AppearAnimationUtils mAppearAnimationUtils;
|
||||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||||
@@ -148,6 +153,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
|
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
|
||||||
mDetailsText = intent.getCharSequenceExtra(
|
mDetailsText = intent.getCharSequenceExtra(
|
||||||
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
|
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
|
||||||
|
mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
|
||||||
}
|
}
|
||||||
if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) {
|
if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) {
|
||||||
mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
|
mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
|
||||||
@@ -174,7 +180,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
// ability to disable the pattern in L. Remove this block after
|
// ability to disable the pattern in L. Remove this block after
|
||||||
// ensuring it's safe to do so. (Note that ConfirmLockPassword
|
// ensuring it's safe to do so. (Note that ConfirmLockPassword
|
||||||
// doesn't have this).
|
// doesn't have this).
|
||||||
if (!mFrp && !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
|
if (!mFrp && !mRemoteValidation
|
||||||
|
&& !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
|
||||||
getActivity().setResult(Activity.RESULT_OK);
|
getActivity().setResult(Activity.RESULT_OK);
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
}
|
}
|
||||||
@@ -203,12 +210,33 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
|
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
// ProgressBar visibility is set to GONE until interacted with.
|
||||||
|
// Set progress bar to INVISIBLE, so the pattern does not get bumped down later.
|
||||||
|
mGlifLayout.setProgressBarShown(false);
|
||||||
|
// Lock pattern is generally not visible until the user has set a lockscreen for the
|
||||||
|
// first time. For a new user, this means that the pattern will always be hidden.
|
||||||
|
// Despite this prerequisite, we want to show the pattern anyway for this flow.
|
||||||
|
mLockPatternView.setInStealthMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
if (mCheckBox != null) {
|
||||||
|
mCheckBox.setText(TextUtils.isEmpty(mCheckBoxLabel)
|
||||||
|
? getDefaultCheckboxLabel()
|
||||||
|
: mCheckBoxLabel);
|
||||||
|
}
|
||||||
|
if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
|
||||||
|
mCancelButton.setText(R.string.lockpassword_forgot_pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mForgotButton != null) {
|
if (mForgotButton != null) {
|
||||||
mForgotButton.setText(R.string.lockpassword_forgot_pattern);
|
mForgotButton.setText(R.string.lockpassword_forgot_pattern);
|
||||||
}
|
}
|
||||||
@@ -271,6 +299,10 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
if (mFrp) {
|
if (mFrp) {
|
||||||
return getString(R.string.lockpassword_confirm_your_pattern_details_frp);
|
return getString(R.string.lockpassword_confirm_your_pattern_details_frp);
|
||||||
}
|
}
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
return getString(
|
||||||
|
R.string.lockpassword_remote_validation_pattern_details);
|
||||||
|
}
|
||||||
final boolean isStrongAuthRequired = isStrongAuthRequired();
|
final boolean isStrongAuthRequired = isStrongAuthRequired();
|
||||||
if (mIsManagedProfile) {
|
if (mIsManagedProfile) {
|
||||||
if (isStrongAuthRequired) {
|
if (isStrongAuthRequired) {
|
||||||
@@ -335,11 +367,11 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
} else {
|
} else {
|
||||||
mGlifLayout.setHeaderText(getDefaultHeader());
|
mGlifLayout.setHeaderText(getDefaultHeader());
|
||||||
}
|
}
|
||||||
if (mDetailsText != null) {
|
|
||||||
mGlifLayout.setDescriptionText(mDetailsText);
|
CharSequence detailsText =
|
||||||
} else {
|
mDetailsText == null ? getDefaultDetails() : mDetailsText;
|
||||||
mGlifLayout.setDescriptionText(getDefaultDetails());
|
mGlifLayout.setDescriptionText(detailsText);
|
||||||
}
|
|
||||||
mErrorTextView.setText("");
|
mErrorTextView.setText("");
|
||||||
updateErrorMessage(
|
updateErrorMessage(
|
||||||
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
|
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
|
||||||
@@ -371,7 +403,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
|
|
||||||
private String getDefaultHeader() {
|
private String getDefaultHeader() {
|
||||||
if (mFrp) return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
|
if (mFrp) return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
return getString(R.string.lockpassword_remote_validation_header);
|
||||||
|
}
|
||||||
if (mIsManagedProfile) {
|
if (mIsManagedProfile) {
|
||||||
return mDevicePolicyManager.getResources().getString(
|
return mDevicePolicyManager.getResources().getString(
|
||||||
CONFIRM_WORK_PROFILE_PATTERN_HEADER,
|
CONFIRM_WORK_PROFILE_PATTERN_HEADER,
|
||||||
@@ -381,6 +415,14 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
return getString(R.string.lockpassword_confirm_your_pattern_header);
|
return getString(R.string.lockpassword_confirm_your_pattern_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getDefaultCheckboxLabel() {
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
return getString(R.string.lockpassword_remote_validation_set_pattern_as_screenlock);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Trying to get default checkbox label for illegal flow");
|
||||||
|
}
|
||||||
|
|
||||||
private Runnable mClearPatternRunnable = new Runnable() {
|
private Runnable mClearPatternRunnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
mLockPatternView.clearPattern();
|
mLockPatternView.clearPattern();
|
||||||
@@ -453,6 +495,13 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
mLockPatternView.setEnabled(false);
|
mLockPatternView.setEnabled(false);
|
||||||
|
|
||||||
final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
|
final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
|
||||||
|
|
||||||
|
if (mRemoteValidation) {
|
||||||
|
validateGuess(credential);
|
||||||
|
mGlifLayout.setProgressBarShown(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(b/161956762): Sanitize this
|
// TODO(b/161956762): Sanitize this
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
if (mReturnGatekeeperPassword) {
|
if (mReturnGatekeeperPassword) {
|
||||||
@@ -563,6 +612,44 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemoteDeviceCredentialValidationResult(
|
||||||
|
RemoteLockscreenValidationResult result) {
|
||||||
|
switch (result.getResultCode()) {
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
|
||||||
|
if (mCheckBox.isChecked()) {
|
||||||
|
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
|
||||||
|
ChooseLockPattern.SaveAndFinishWorker saveAndFinishWorker =
|
||||||
|
new ChooseLockPattern.SaveAndFinishWorker();
|
||||||
|
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
|
||||||
|
.commit();
|
||||||
|
getFragmentManager().executePendingTransactions();
|
||||||
|
saveAndFinishWorker.setListener(this);
|
||||||
|
saveAndFinishWorker.start(
|
||||||
|
mLockPatternUtils,
|
||||||
|
/* requestGatekeeperPassword= */ false,
|
||||||
|
mDeviceCredentialGuess,
|
||||||
|
/* currentCredential= */ null,
|
||||||
|
mEffectiveUserId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||||
|
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||||
|
break;
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
|
||||||
|
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||||
|
break;
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_LOCKOUT:
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
|
||||||
|
(int) result.getTimeoutMillis(), mEffectiveUserId);
|
||||||
|
break;
|
||||||
|
case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
mGlifLayout.setProgressBarShown(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||||
int effectiveUserId, boolean newResult) {
|
int effectiveUserId, boolean newResult) {
|
||||||
@@ -632,5 +719,19 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
|||||||
appearing, interpolator, finishListener);
|
appearing, interpolator, finishListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for when the device credential guess used for remote validation was set as the
|
||||||
|
* current device's device credential.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||||
|
if (mDeviceCredentialGuess != null) {
|
||||||
|
mDeviceCredentialGuess.zeroize();
|
||||||
|
}
|
||||||
|
mGlifLayout.setProgressBarShown(false);
|
||||||
|
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||||
|
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
package com.android.settings.password;
|
package com.android.settings.password;
|
||||||
|
|
||||||
|
import static com.android.settings.password.TestUtils.COMPONENT_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
|
||||||
|
import static com.android.settings.password.TestUtils.createStartLockscreenValidationRequest;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -10,6 +14,8 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.StartLockscreenValidationRequest;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -160,6 +166,93 @@ public class ChooseLockSettingsHelperTest {
|
|||||||
startedIntent.getComponent());
|
startedIntent.getComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void launchConfirmPassword_remoteValidation_passwordLockType() throws Exception {
|
||||||
|
Activity activity = Robolectric.setupActivity(Activity.class);
|
||||||
|
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||||
|
StartLockscreenValidationRequest request = createStartLockscreenValidationRequest(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS);
|
||||||
|
|
||||||
|
ChooseLockSettingsHelper chooseLockSettingsHelper = getChooseLockSettingsHelper(
|
||||||
|
new ChooseLockSettingsHelper.Builder(activity)
|
||||||
|
.setRemoteLockscreenValidation(true)
|
||||||
|
.setStartLockscreenValidationRequest(request)
|
||||||
|
.setRemoteLockscreenValidationServiceComponent(COMPONENT_NAME));
|
||||||
|
chooseLockSettingsHelper.launch();
|
||||||
|
|
||||||
|
Intent startedIntent = shadowActivity.getNextStartedActivity();
|
||||||
|
assertEquals(new ComponentName("com.android.settings",
|
||||||
|
ConfirmLockPassword.class.getName()), startedIntent.getComponent());
|
||||||
|
assertThat(startedIntent.getBooleanExtra(
|
||||||
|
ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, false)
|
||||||
|
).isTrue();
|
||||||
|
assertThat(startedIntent.getParcelableExtra(
|
||||||
|
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
StartLockscreenValidationRequest.class)
|
||||||
|
).isEqualTo(request);
|
||||||
|
assertThat(startedIntent.getParcelableExtra(
|
||||||
|
Intent.EXTRA_COMPONENT_NAME, ComponentName.class)
|
||||||
|
).isEqualTo(COMPONENT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void launchConfirmPassword_remoteValidation_pinLockType() throws Exception {
|
||||||
|
Activity activity = Robolectric.setupActivity(Activity.class);
|
||||||
|
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||||
|
StartLockscreenValidationRequest request = createStartLockscreenValidationRequest(
|
||||||
|
KeyguardManager.PIN, VALID_REMAINING_ATTEMPTS);
|
||||||
|
|
||||||
|
ChooseLockSettingsHelper chooseLockSettingsHelper = getChooseLockSettingsHelper(
|
||||||
|
new ChooseLockSettingsHelper.Builder(activity)
|
||||||
|
.setRemoteLockscreenValidation(true)
|
||||||
|
.setStartLockscreenValidationRequest(request)
|
||||||
|
.setRemoteLockscreenValidationServiceComponent(COMPONENT_NAME));
|
||||||
|
chooseLockSettingsHelper.launch();
|
||||||
|
|
||||||
|
Intent startedIntent = shadowActivity.getNextStartedActivity();
|
||||||
|
assertEquals(new ComponentName("com.android.settings",
|
||||||
|
ConfirmLockPassword.class.getName()), startedIntent.getComponent());
|
||||||
|
assertThat(startedIntent.getBooleanExtra(
|
||||||
|
ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, false)
|
||||||
|
).isTrue();
|
||||||
|
assertThat(startedIntent.getParcelableExtra(
|
||||||
|
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
StartLockscreenValidationRequest.class)
|
||||||
|
).isEqualTo(request);
|
||||||
|
assertThat(startedIntent.getParcelableExtra(
|
||||||
|
Intent.EXTRA_COMPONENT_NAME, ComponentName.class)
|
||||||
|
).isEqualTo(COMPONENT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void launchConfirmPattern_remoteValidation_patternLockType() throws Exception {
|
||||||
|
Activity activity = Robolectric.setupActivity(Activity.class);
|
||||||
|
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||||
|
StartLockscreenValidationRequest request = createStartLockscreenValidationRequest(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS);
|
||||||
|
|
||||||
|
ChooseLockSettingsHelper chooseLockSettingsHelper = getChooseLockSettingsHelper(
|
||||||
|
new ChooseLockSettingsHelper.Builder(activity)
|
||||||
|
.setRemoteLockscreenValidation(true)
|
||||||
|
.setStartLockscreenValidationRequest(request)
|
||||||
|
.setRemoteLockscreenValidationServiceComponent(COMPONENT_NAME));
|
||||||
|
chooseLockSettingsHelper.launch();
|
||||||
|
|
||||||
|
Intent startedIntent = shadowActivity.getNextStartedActivity();
|
||||||
|
assertEquals(new ComponentName("com.android.settings",
|
||||||
|
ConfirmLockPattern.class.getName()), startedIntent.getComponent());
|
||||||
|
assertThat(startedIntent.getBooleanExtra(
|
||||||
|
ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, false)
|
||||||
|
).isTrue();
|
||||||
|
assertThat(startedIntent.getParcelableExtra(
|
||||||
|
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
StartLockscreenValidationRequest.class)
|
||||||
|
).isEqualTo(request);
|
||||||
|
assertThat(startedIntent.getParcelableExtra(
|
||||||
|
Intent.EXTRA_COMPONENT_NAME, ComponentName.class)
|
||||||
|
).isEqualTo(COMPONENT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
private ChooseLockSettingsHelper getChooseLockSettingsHelper(
|
private ChooseLockSettingsHelper getChooseLockSettingsHelper(
|
||||||
ChooseLockSettingsHelper.Builder builder) {
|
ChooseLockSettingsHelper.Builder builder) {
|
||||||
LockPatternUtils mockLockPatternUtils = mock(LockPatternUtils.class);
|
LockPatternUtils mockLockPatternUtils = mock(LockPatternUtils.class);
|
||||||
|
@@ -16,25 +16,162 @@
|
|||||||
|
|
||||||
package com.android.settings.password;
|
package com.android.settings.password;
|
||||||
|
|
||||||
|
import static com.android.settings.password.TestUtils.NO_MORE_REMAINING_ATTEMPTS;
|
||||||
|
import static com.android.settings.password.TestUtils.PACKAGE_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.SERVICE_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
|
||||||
|
import static com.android.settings.password.TestUtils.buildConfirmDeviceCredentialBaseActivity;
|
||||||
|
import static com.android.settings.password.TestUtils.createPackageInfoWithService;
|
||||||
|
import static com.android.settings.password.TestUtils.createRemoteLockscreenValidationIntent;
|
||||||
|
import static com.android.settings.password.TestUtils.createStartLockscreenValidationRequest;
|
||||||
|
import static com.android.settings.password.TestUtils.getConfirmDeviceCredentialBaseFragment;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.admin.ManagedSubscriptionsPolicy;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.FeatureFlagUtils;
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.settings.password.ConfirmDeviceCredentialBaseFragment.LastTryDialog;
|
import com.android.settings.password.ConfirmDeviceCredentialBaseFragment.LastTryDialog;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.Shadows;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadows.ShadowApplicationPackageManager;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {
|
||||||
|
ShadowLockPatternUtils.class,
|
||||||
|
ShadowUtils.class,
|
||||||
|
ShadowDevicePolicyManager.class,
|
||||||
|
ShadowUserManager.class,
|
||||||
|
ShadowApplicationPackageManager.class
|
||||||
|
})
|
||||||
public class ConfirmCredentialTest {
|
public class ConfirmCredentialTest {
|
||||||
|
|
||||||
private Context mContext = RuntimeEnvironment.application;
|
private Context mContext;
|
||||||
|
private ShadowApplicationPackageManager mShadowApplicationPackageManager;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
mShadowApplicationPackageManager =
|
||||||
|
(ShadowApplicationPackageManager) Shadows.shadowOf(mContext.getPackageManager());
|
||||||
|
mShadowApplicationPackageManager.addPackageNoDefaults(
|
||||||
|
TestUtils.createPackageInfoWithService(
|
||||||
|
PACKAGE_NAME, SERVICE_NAME,
|
||||||
|
Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE));
|
||||||
|
|
||||||
|
final ShadowDevicePolicyManager shadowDpm = ShadowDevicePolicyManager.getShadow();
|
||||||
|
shadowDpm.setManagedSubscriptionsPolicy(
|
||||||
|
new ManagedSubscriptionsPolicy(
|
||||||
|
ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS));
|
||||||
|
|
||||||
|
FeatureFlagUtils.setEnabled(mContext,
|
||||||
|
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_successfullyStart() {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(ConfirmLockPassword.class, new Intent());
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isFalse();
|
||||||
|
assertThat(fragment.mRemoteValidation).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_successfullyStart() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class, createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isFalse();
|
||||||
|
assertThat(fragment.mRemoteValidation).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_flagDisabled_finishActivity() throws Exception {
|
||||||
|
FeatureFlagUtils.setEnabled(mContext,
|
||||||
|
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, false);
|
||||||
|
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_invalidServiceComponentName_finishActivity()
|
||||||
|
throws Exception {
|
||||||
|
Intent intentWithInvalidComponentName = new Intent()
|
||||||
|
.putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, true)
|
||||||
|
.putExtra(KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
createStartLockscreenValidationRequest(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS))
|
||||||
|
.putExtra(Intent.EXTRA_COMPONENT_NAME, new ComponentName("pkg", "cls"));
|
||||||
|
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class, intentWithInvalidComponentName);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_serviceDoesNotRequestCorrectPermission_finishActivity()
|
||||||
|
throws Exception {
|
||||||
|
// Remove package with valid ServiceInfo
|
||||||
|
mShadowApplicationPackageManager.removePackage(PACKAGE_NAME);
|
||||||
|
// Add a service that does not request the BIND_REMOTE_LOCKSCREEN_SERVICE permission
|
||||||
|
mShadowApplicationPackageManager.addPackageNoDefaults(
|
||||||
|
createPackageInfoWithService(
|
||||||
|
PACKAGE_NAME,
|
||||||
|
SERVICE_NAME,
|
||||||
|
Manifest.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE));
|
||||||
|
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_noMoreAttempts_finishActivity() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, NO_MORE_REMAINING_ATTEMPTS));
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLastTryDialogShownExactlyOnce() {
|
public void testLastTryDialogShownExactlyOnce() {
|
||||||
|
@@ -0,0 +1,308 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 static com.android.settings.password.ConfirmLockPassword.ConfirmLockPasswordFragment;
|
||||||
|
import static com.android.settings.password.TestUtils.GUESS_INVALID_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.GUESS_VALID_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.LOCKOUT_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.NO_REMAINING_ATTEMPTS_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.PACKAGE_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.SERVICE_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.TIMEOUT_MS;
|
||||||
|
import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
|
||||||
|
import static com.android.settings.password.TestUtils.buildConfirmDeviceCredentialBaseActivity;
|
||||||
|
import static com.android.settings.password.TestUtils.createRemoteLockscreenValidationIntent;
|
||||||
|
import static com.android.settings.password.TestUtils.getConfirmDeviceCredentialBaseFragment;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.admin.ManagedSubscriptionsPolicy;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
|
||||||
|
import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.util.FeatureFlagUtils;
|
||||||
|
import android.widget.ImeAwareEditText;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.Shadows;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadows.ShadowApplicationPackageManager;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {
|
||||||
|
ShadowLockPatternUtils.class,
|
||||||
|
ShadowUtils.class,
|
||||||
|
ShadowDevicePolicyManager.class,
|
||||||
|
ShadowUserManager.class,
|
||||||
|
ShadowApplicationPackageManager.class
|
||||||
|
})
|
||||||
|
public class ConfirmLockPasswordTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
CredentialCheckResultTracker mCredentialCheckResultTracker;
|
||||||
|
@Mock
|
||||||
|
RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<IRemoteLockscreenValidationCallback> mCallbackCaptor;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private LockPatternUtils mLockPatternUtils;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mLockPatternUtils = new LockPatternUtils(mContext);
|
||||||
|
|
||||||
|
ShadowApplicationPackageManager shadowApplicationPackageManager =
|
||||||
|
(ShadowApplicationPackageManager) Shadows.shadowOf(mContext.getPackageManager());
|
||||||
|
shadowApplicationPackageManager.addPackageNoDefaults(
|
||||||
|
TestUtils.createPackageInfoWithService(
|
||||||
|
PACKAGE_NAME,
|
||||||
|
SERVICE_NAME,
|
||||||
|
Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE));
|
||||||
|
|
||||||
|
final ShadowDevicePolicyManager shadowDpm = ShadowDevicePolicyManager.getShadow();
|
||||||
|
shadowDpm.setManagedSubscriptionsPolicy(
|
||||||
|
new ManagedSubscriptionsPolicy(
|
||||||
|
ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS));
|
||||||
|
|
||||||
|
// Set false by default so we can check if lock was set when remote validation succeeds.
|
||||||
|
ShadowLockPatternUtils.setIsSecure(UserHandle.myUserId(), false);
|
||||||
|
|
||||||
|
FeatureFlagUtils.setEnabled(mContext,
|
||||||
|
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowLockPatternUtils.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_password_successfullyStart() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isFalse();
|
||||||
|
assertThat(fragment.mRemoteValidation).isTrue();
|
||||||
|
ImeAwareEditText editText = (ImeAwareEditText) activity.findViewById(R.id.password_entry);
|
||||||
|
assertThat(editText.getInputType()).isEqualTo(
|
||||||
|
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_pin_successfullyStart() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PIN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isFalse();
|
||||||
|
assertThat(fragment.mRemoteValidation).isTrue();
|
||||||
|
ImeAwareEditText editText = (ImeAwareEditText) activity.findViewById(R.id.password_entry);
|
||||||
|
assertThat(editText.getInputType()).isEqualTo(
|
||||||
|
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleNext_normalFlow_doesNotAttemptRemoteLockscreenValidation() {
|
||||||
|
ConfirmLockPassword activity = Robolectric.buildActivity(
|
||||||
|
ConfirmLockPassword.class, new Intent()).setup().get();
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerHandleNext(fragment, passwordEntry);
|
||||||
|
|
||||||
|
verifyNoInteractions(mRemoteLockscreenValidationClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleNext_remoteValidation_correctGuess_checkboxChecked() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerHandleNext(fragment, passwordEntry);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
|
||||||
|
assertThat(fragment.mDeviceCredentialGuess).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleNext_remoteValidation_correctGuess_checkboxUnchecked() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
fragment.mCheckBox.setChecked(false);
|
||||||
|
ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerHandleNext(fragment, passwordEntry);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
assertThat(fragment.mDeviceCredentialGuess).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleNext_remoteValidation_guessInvalid() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerHandleNext(fragment, passwordEntry);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(GUESS_INVALID_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(false), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleNext_remoteValidation_lockout() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerHandleNext(fragment, passwordEntry);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(LOCKOUT_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(false), any(), eq(TIMEOUT_MS), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleNext_remoteValidation_noRemainingAttempts_finishActivity() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPassword.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmLockPasswordFragment fragment =
|
||||||
|
(ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerHandleNext(fragment, passwordEntry);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(NO_REMAINING_ATTEMPTS_RESULT);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isTrue();
|
||||||
|
verify(mCredentialCheckResultTracker, never())
|
||||||
|
.setResult(anyBoolean(), any(), anyInt(), anyInt());
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerHandleNext(
|
||||||
|
ConfirmLockPasswordFragment fragment, ImeAwareEditText passwordEntry) {
|
||||||
|
passwordEntry.setText("Password");
|
||||||
|
ReflectionHelpers.callInstanceMethod(fragment, "handleNext");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,286 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 static com.android.settings.password.TestUtils.GUESS_INVALID_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.GUESS_VALID_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.LOCKOUT_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.NO_REMAINING_ATTEMPTS_RESULT;
|
||||||
|
import static com.android.settings.password.TestUtils.PACKAGE_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.SERVICE_NAME;
|
||||||
|
import static com.android.settings.password.TestUtils.TIMEOUT_MS;
|
||||||
|
import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
|
||||||
|
import static com.android.settings.password.TestUtils.buildConfirmDeviceCredentialBaseActivity;
|
||||||
|
import static com.android.settings.password.TestUtils.createPackageInfoWithService;
|
||||||
|
import static com.android.settings.password.TestUtils.createRemoteLockscreenValidationIntent;
|
||||||
|
import static com.android.settings.password.TestUtils.getConfirmDeviceCredentialBaseFragment;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.app.admin.ManagedSubscriptionsPolicy;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
|
||||||
|
import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
|
||||||
|
import android.util.FeatureFlagUtils;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.internal.widget.LockPatternView;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.Shadows;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadows.ShadowApplicationPackageManager;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {
|
||||||
|
ShadowLockPatternUtils.class,
|
||||||
|
ShadowUtils.class,
|
||||||
|
ShadowDevicePolicyManager.class,
|
||||||
|
ShadowUserManager.class,
|
||||||
|
ShadowApplicationPackageManager.class
|
||||||
|
})
|
||||||
|
public class ConfirmLockPatternTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
CredentialCheckResultTracker mCredentialCheckResultTracker;
|
||||||
|
@Mock
|
||||||
|
RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<IRemoteLockscreenValidationCallback> mCallbackCaptor;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private LockPatternUtils mLockPatternUtils;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mLockPatternUtils = new LockPatternUtils(mContext);
|
||||||
|
|
||||||
|
ShadowApplicationPackageManager shadowApplicationPackageManager =
|
||||||
|
(ShadowApplicationPackageManager) Shadows.shadowOf(mContext.getPackageManager());
|
||||||
|
shadowApplicationPackageManager.addPackageNoDefaults(
|
||||||
|
createPackageInfoWithService(
|
||||||
|
PACKAGE_NAME,
|
||||||
|
SERVICE_NAME,
|
||||||
|
Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE));
|
||||||
|
|
||||||
|
final ShadowDevicePolicyManager shadowDpm = ShadowDevicePolicyManager.getShadow();
|
||||||
|
shadowDpm.setManagedSubscriptionsPolicy(
|
||||||
|
new ManagedSubscriptionsPolicy(
|
||||||
|
ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS));
|
||||||
|
|
||||||
|
// Set false by default so we can check if lock was set when remote validation succeeds.
|
||||||
|
ShadowLockPatternUtils.setIsSecure(UserHandle.myUserId(), false);
|
||||||
|
|
||||||
|
FeatureFlagUtils.setEnabled(mContext,
|
||||||
|
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowLockPatternUtils.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreate_remoteValidation_successfullyStart() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPattern.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isFalse();
|
||||||
|
assertThat(fragment.mRemoteValidation).isTrue();
|
||||||
|
LockPatternView lockPatternView = (LockPatternView) activity.findViewById(R.id.lockPattern);
|
||||||
|
assertThat(lockPatternView.isInStealthMode()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPatternDetected_normalFlow_doesNotAttemptRemoteLockscreenValidation() {
|
||||||
|
ConfirmLockPattern activity = Robolectric.buildActivity(
|
||||||
|
ConfirmLockPattern.class, new Intent()).setup().get();
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerOnPatternDetected(lockPatternView);
|
||||||
|
|
||||||
|
verifyNoInteractions(mRemoteLockscreenValidationClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPatternDetected_remoteValidation_guessValid_checkboxChecked() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPattern.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerOnPatternDetected(lockPatternView);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
|
||||||
|
assertThat(fragment.mDeviceCredentialGuess).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPatternDetected_remoteValidation_guessValid_checkboxUnchecked() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPattern.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
fragment.mCheckBox.setChecked(false);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerOnPatternDetected(lockPatternView);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
assertThat(fragment.mDeviceCredentialGuess).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPatternDetected_remoteValidation_guessInvalid() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPattern.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerOnPatternDetected(lockPatternView);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(GUESS_INVALID_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(false), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPatternDetected_remoteValidation_lockout() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPattern.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerOnPatternDetected(lockPatternView);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(LOCKOUT_RESULT);
|
||||||
|
|
||||||
|
verify(mCredentialCheckResultTracker).setResult(
|
||||||
|
eq(false), any(), eq(TIMEOUT_MS), eq(fragment.mEffectiveUserId));
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPatternDetected_noRemainingAttempts_finishActivity() throws Exception {
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity =
|
||||||
|
buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
ConfirmLockPattern.class,
|
||||||
|
createRemoteLockscreenValidationIntent(
|
||||||
|
KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
|
||||||
|
ConfirmDeviceCredentialBaseFragment fragment =
|
||||||
|
getConfirmDeviceCredentialBaseFragment(activity);
|
||||||
|
LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
|
||||||
|
ReflectionHelpers.setField(fragment,
|
||||||
|
"mCredentialCheckResultTracker", mCredentialCheckResultTracker);
|
||||||
|
fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
|
||||||
|
|
||||||
|
triggerOnPatternDetected(lockPatternView);
|
||||||
|
verify(mRemoteLockscreenValidationClient)
|
||||||
|
.validateLockscreenGuess(any(), mCallbackCaptor.capture());
|
||||||
|
mCallbackCaptor.getValue().onSuccess(NO_REMAINING_ATTEMPTS_RESULT);
|
||||||
|
|
||||||
|
assertThat(activity.isFinishing()).isTrue();
|
||||||
|
verifyNoInteractions(mCredentialCheckResultTracker);
|
||||||
|
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerOnPatternDetected(LockPatternView lockPatternView) {
|
||||||
|
List<LockPatternView.Cell> pattern = List.of(LockPatternView.Cell.of(0, 0));
|
||||||
|
lockPatternView.setPattern(LockPatternView.DisplayMode.Correct, pattern);
|
||||||
|
ReflectionHelpers.callInstanceMethod(lockPatternView, "notifyPatternDetected");
|
||||||
|
}
|
||||||
|
}
|
107
tests/robotests/src/com/android/settings/password/TestUtils.java
Normal file
107
tests/robotests/src/com/android/settings/password/TestUtils.java
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.app.KeyguardManager;
|
||||||
|
import android.app.RemoteLockscreenValidationResult;
|
||||||
|
import android.app.StartLockscreenValidationRequest;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
|
||||||
|
import com.android.security.SecureBox;
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public final class TestUtils {
|
||||||
|
|
||||||
|
public static final String SERVICE_NAME = "SERVICE_NAME";
|
||||||
|
public static final String PACKAGE_NAME = "PACKAGE_NAME";
|
||||||
|
public static final ComponentName COMPONENT_NAME =
|
||||||
|
new ComponentName(PACKAGE_NAME, SERVICE_NAME);
|
||||||
|
public static final int VALID_REMAINING_ATTEMPTS = 5;
|
||||||
|
public static final int NO_MORE_REMAINING_ATTEMPTS = 0;
|
||||||
|
public static final int TIMEOUT_MS = 10000;
|
||||||
|
public static final RemoteLockscreenValidationResult GUESS_VALID_RESULT =
|
||||||
|
new RemoteLockscreenValidationResult.Builder()
|
||||||
|
.setResultCode(RemoteLockscreenValidationResult.RESULT_GUESS_VALID)
|
||||||
|
.build();
|
||||||
|
public static final RemoteLockscreenValidationResult GUESS_INVALID_RESULT =
|
||||||
|
new RemoteLockscreenValidationResult.Builder()
|
||||||
|
.setResultCode(RemoteLockscreenValidationResult.RESULT_GUESS_INVALID)
|
||||||
|
.build();
|
||||||
|
public static final RemoteLockscreenValidationResult LOCKOUT_RESULT =
|
||||||
|
new RemoteLockscreenValidationResult.Builder()
|
||||||
|
.setResultCode(RemoteLockscreenValidationResult.RESULT_LOCKOUT)
|
||||||
|
.setTimeoutMillis(TIMEOUT_MS)
|
||||||
|
.build();
|
||||||
|
public static final RemoteLockscreenValidationResult NO_REMAINING_ATTEMPTS_RESULT =
|
||||||
|
new RemoteLockscreenValidationResult.Builder()
|
||||||
|
.setResultCode(RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private TestUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PackageInfo createPackageInfoWithService(
|
||||||
|
String packageName, String serviceName, String requiredServicePermission) {
|
||||||
|
ServiceInfo serviceInfo = new ServiceInfo();
|
||||||
|
serviceInfo.name = serviceName;
|
||||||
|
serviceInfo.applicationInfo = new ApplicationInfo();
|
||||||
|
serviceInfo.permission = requiredServicePermission;
|
||||||
|
|
||||||
|
PackageInfo packageInfo = new PackageInfo();
|
||||||
|
packageInfo.packageName = packageName;
|
||||||
|
packageInfo.services = new ServiceInfo[]{serviceInfo};
|
||||||
|
return packageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Intent createRemoteLockscreenValidationIntent(
|
||||||
|
int lockscreenType, int remainingAttempts) throws Exception {
|
||||||
|
return new Intent()
|
||||||
|
.putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, true)
|
||||||
|
.putExtra(KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||||
|
createStartLockscreenValidationRequest(lockscreenType, remainingAttempts))
|
||||||
|
.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StartLockscreenValidationRequest createStartLockscreenValidationRequest(
|
||||||
|
int lockscreenType, int remainingAttempts) throws NoSuchAlgorithmException {
|
||||||
|
return new StartLockscreenValidationRequest.Builder()
|
||||||
|
.setLockscreenUiType(lockscreenType)
|
||||||
|
.setRemainingAttempts(remainingAttempts)
|
||||||
|
.setSourcePublicKey(SecureBox.genKeyPair().getPublic().getEncoded())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfirmDeviceCredentialBaseActivity buildConfirmDeviceCredentialBaseActivity(
|
||||||
|
Class<? extends ConfirmDeviceCredentialBaseActivity> impl, Intent intent) {
|
||||||
|
return Robolectric.buildActivity(impl, intent).setup().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfirmDeviceCredentialBaseFragment getConfirmDeviceCredentialBaseFragment(
|
||||||
|
ConfirmDeviceCredentialBaseActivity activity) {
|
||||||
|
return (ConfirmDeviceCredentialBaseFragment)
|
||||||
|
activity.getSupportFragmentManager().findFragmentById(R.id.main_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import android.annotation.Nullable;
|
|||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
|
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
|
||||||
|
import android.app.admin.ManagedSubscriptionsPolicy;
|
||||||
import android.app.admin.PasswordMetrics;
|
import android.app.admin.PasswordMetrics;
|
||||||
import android.app.admin.PasswordPolicy;
|
import android.app.admin.PasswordPolicy;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -29,6 +30,7 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
|||||||
private Map<Integer, CharSequence> mSupportMessagesMap = new HashMap<>();
|
private Map<Integer, CharSequence> mSupportMessagesMap = new HashMap<>();
|
||||||
private boolean mIsAdminActiveAsUser = false;
|
private boolean mIsAdminActiveAsUser = false;
|
||||||
private ComponentName mDeviceOwnerComponentName;
|
private ComponentName mDeviceOwnerComponentName;
|
||||||
|
private ManagedSubscriptionsPolicy mManagedSubscriptionsPolicy;
|
||||||
private int mDeviceOwnerUserId = -1;
|
private int mDeviceOwnerUserId = -1;
|
||||||
private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
|
private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
|
||||||
private int mPasswordMinLength = 0;
|
private int mPasswordMinLength = 0;
|
||||||
@@ -85,6 +87,10 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
|||||||
mDeviceOwnerTypes.put(admin.getPackageName(), deviceOwnerType);
|
mDeviceOwnerTypes.put(admin.getPackageName(), deviceOwnerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
|
||||||
|
mManagedSubscriptionsPolicy = policy;
|
||||||
|
}
|
||||||
|
|
||||||
@DeviceOwnerType
|
@DeviceOwnerType
|
||||||
public int getDeviceOwnerType(@NonNull ComponentName admin) {
|
public int getDeviceOwnerType(@NonNull ComponentName admin) {
|
||||||
return mDeviceOwnerTypes.getOrDefault(admin.getPackageName(), DEVICE_OWNER_TYPE_DEFAULT);
|
return mDeviceOwnerTypes.getOrDefault(admin.getPackageName(), DEVICE_OWNER_TYPE_DEFAULT);
|
||||||
@@ -99,6 +105,11 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
|||||||
return policy.getMinMetrics();
|
return policy.getMinMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
|
||||||
|
return mManagedSubscriptionsPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
public void setPasswordQuality(int quality) {
|
public void setPasswordQuality(int quality) {
|
||||||
mPasswordMinQuality = quality;
|
mPasswordMinQuality = quality;
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.testutils.shadow;
|
package com.android.settings.testutils.shadow;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.admin.PasswordMetrics;
|
import android.app.admin.PasswordMetrics;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -43,6 +45,10 @@ public class ShadowLockPatternUtils {
|
|||||||
private static Map<Integer, PasswordMetrics> sUserToMetricsMap = new HashMap<>();
|
private static Map<Integer, PasswordMetrics> sUserToMetricsMap = new HashMap<>();
|
||||||
private static Map<Integer, PasswordMetrics> sUserToProfileMetricsMap = new HashMap<>();
|
private static Map<Integer, PasswordMetrics> sUserToProfileMetricsMap = new HashMap<>();
|
||||||
private static Map<Integer, Boolean> sUserToIsSecureMap = new HashMap<>();
|
private static Map<Integer, Boolean> sUserToIsSecureMap = new HashMap<>();
|
||||||
|
private static Map<Integer, Boolean> sUserToPatternEverChosenMap = new HashMap<>();
|
||||||
|
private static Map<Integer, Boolean> sUserToVisiblePatternEnabledMap = new HashMap<>();
|
||||||
|
private static Map<Integer, Boolean> sUserToBiometricAllowedMap = new HashMap<>();
|
||||||
|
private static Map<Integer, Boolean> sUserToLockPatternEnabledMap = new HashMap<>();
|
||||||
|
|
||||||
private static boolean sIsUserOwnsFrpCredential;
|
private static boolean sIsUserOwnsFrpCredential;
|
||||||
|
|
||||||
@@ -53,6 +59,10 @@ public class ShadowLockPatternUtils {
|
|||||||
sUserToMetricsMap.clear();
|
sUserToMetricsMap.clear();
|
||||||
sUserToProfileMetricsMap.clear();
|
sUserToProfileMetricsMap.clear();
|
||||||
sUserToIsSecureMap.clear();
|
sUserToIsSecureMap.clear();
|
||||||
|
sUserToPatternEverChosenMap.clear();
|
||||||
|
sUserToVisiblePatternEnabledMap.clear();
|
||||||
|
sUserToBiometricAllowedMap.clear();
|
||||||
|
sUserToLockPatternEnabledMap.clear();
|
||||||
sDeviceEncryptionEnabled = false;
|
sDeviceEncryptionEnabled = false;
|
||||||
sIsUserOwnsFrpCredential = false;
|
sIsUserOwnsFrpCredential = false;
|
||||||
}
|
}
|
||||||
@@ -136,6 +146,56 @@ public class ShadowLockPatternUtils {
|
|||||||
sIsUserOwnsFrpCredential = isUserOwnsFrpCredential;
|
sIsUserOwnsFrpCredential = isUserOwnsFrpCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public boolean isVisiblePatternEnabled(int userId) {
|
||||||
|
return sUserToVisiblePatternEnabledMap.getOrDefault(userId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setIsVisiblePatternEnabled(int userId, boolean isVisiblePatternEnabled) {
|
||||||
|
sUserToVisiblePatternEnabledMap.put(userId, isVisiblePatternEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public boolean isPatternEverChosen(int userId) {
|
||||||
|
return sUserToPatternEverChosenMap.getOrDefault(userId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setIsPatternEverChosen(int userId, boolean isPatternEverChosen) {
|
||||||
|
sUserToPatternEverChosenMap.put(userId, isPatternEverChosen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public boolean isBiometricAllowedForUser(int userId) {
|
||||||
|
return sUserToBiometricAllowedMap.getOrDefault(userId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setIsBiometricAllowedForUser(int userId, boolean isBiometricAllowed) {
|
||||||
|
sUserToBiometricAllowedMap.put(userId, isBiometricAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public boolean isLockPatternEnabled(int userId) {
|
||||||
|
return sUserToBiometricAllowedMap.getOrDefault(userId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setIsLockPatternEnabled(int userId, boolean isLockPatternEnabled) {
|
||||||
|
sUserToLockPatternEnabledMap.put(userId, isLockPatternEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
|
||||||
|
@NonNull LockscreenCredential savedCredential, int userHandle) {
|
||||||
|
setIsSecure(userHandle, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
|
||||||
|
@Nullable LockPatternUtils.CheckCredentialProgressCallback progressCallback)
|
||||||
|
throws LockPatternUtils.RequestThrottledException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setRequiredPasswordComplexity(int userHandle, int complexity) {
|
public static void setRequiredPasswordComplexity(int userHandle, int complexity) {
|
||||||
sUserToComplexityMap.put(userHandle, complexity);
|
sUserToComplexityMap.put(userHandle, complexity);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user