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:
Brian Lee
2022-12-05 15:58:01 -08:00
parent dff3e4ed74
commit d4f8e5802e
18 changed files with 1582 additions and 71 deletions

View File

@@ -1,5 +1,9 @@
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 org.junit.Assert.assertEquals;
@@ -10,6 +14,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.StartLockscreenValidationRequest;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
@@ -160,6 +166,93 @@ public class ChooseLockSettingsHelperTest {
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(
ChooseLockSettingsHelper.Builder builder) {
LockPatternUtils mockLockPatternUtils = mock(LockPatternUtils.class);

View File

@@ -16,25 +16,162 @@
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 android.Manifest;
import android.app.KeyguardManager;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.FeatureFlagUtils;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.test.core.app.ApplicationProvider;
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.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
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)
@Config(shadows = {
ShadowLockPatternUtils.class,
ShadowUtils.class,
ShadowDevicePolicyManager.class,
ShadowUserManager.class,
ShadowApplicationPackageManager.class
})
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
public void testLastTryDialogShownExactlyOnce() {

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View 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);
}
}