Add mandatory biometric prompt to platform surfaces (4/N)
1. Enable developer options via build info 2. Enable developer options via toggle under system -> developer options Flag: android.hardware.biometrics.flags.mandatory_biometrics Fixes: 355500452 Test: atest BuildNumberPreferenceControllerTest DevelopmentSettingsDashboardFragmentTest Change-Id: Iecbe34024d287e71e235becec3ce5a2bd5c1697f
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
|
||||
import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
|
||||
import static android.view.flags.Flags.sensitiveContentAppProtectionApi;
|
||||
@@ -100,11 +101,13 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
NfcRebootDialog.OnNfcRebootDialogConfirmedListener, BluetoothSnoopLogHost {
|
||||
|
||||
private static final String TAG = "DevSettingsDashboard";
|
||||
@VisibleForTesting static final int REQUEST_BIOMETRIC_PROMPT = 100;
|
||||
|
||||
private final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore =
|
||||
new BluetoothA2dpConfigStore();
|
||||
|
||||
private boolean mIsAvailable = true;
|
||||
private boolean mIsBiometricsAuthenticated;
|
||||
private SettingsMainSwitchBar mSwitchBar;
|
||||
private DevelopmentSwitchBarController mSwitchBarController;
|
||||
private List<AbstractPreferenceController> mPreferenceControllers = new ArrayList<>();
|
||||
@@ -216,6 +219,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final ContentResolver cr = getContext().getContentResolver();
|
||||
mIsBiometricsAuthenticated = false;
|
||||
cr.registerContentObserver(mDevelopEnabled, false, mDeveloperSettingsObserver);
|
||||
|
||||
// Restore UI state based on whether developer options is enabled
|
||||
@@ -360,7 +364,18 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext());
|
||||
if (isChecked != developmentEnabledState) {
|
||||
if (isChecked) {
|
||||
EnableDevelopmentSettingWarningDialog.show(this /* host */);
|
||||
final int userId = getContext().getUserId();
|
||||
if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getContext(),
|
||||
mIsBiometricsAuthenticated,
|
||||
false /* biometricsAuthenticationRequested */, userId)) {
|
||||
mSwitchBar.setChecked(false);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this,
|
||||
REQUEST_BIOMETRIC_PROMPT, userId, false /* hideBackground */);
|
||||
} else {
|
||||
//Reset biometrics once enable dialog is shown
|
||||
mIsBiometricsAuthenticated = false;
|
||||
EnableDevelopmentSettingWarningDialog.show(this /* host */);
|
||||
}
|
||||
} else {
|
||||
final BluetoothA2dpHwOffloadPreferenceController a2dpController =
|
||||
getDevelopmentOptionsController(
|
||||
@@ -534,6 +549,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
boolean handledResult = false;
|
||||
if (requestCode == REQUEST_BIOMETRIC_PROMPT) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
mIsBiometricsAuthenticated = true;
|
||||
mSwitchBar.setChecked(true);
|
||||
}
|
||||
}
|
||||
for (AbstractPreferenceController controller : mPreferenceControllers) {
|
||||
if (controller instanceof OnActivityResultListener) {
|
||||
// We do not break early because it is possible for multiple controllers to
|
||||
|
@@ -55,6 +55,7 @@ public class BuildNumberPreferenceController extends BasePreferenceController im
|
||||
|
||||
static final int TAPS_TO_BE_A_DEVELOPER = 7;
|
||||
static final int REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF = 100;
|
||||
static final int REQUEST_IDENTITY_CHECK_FOR_DEV_PREF = 101;
|
||||
|
||||
private Activity mActivity;
|
||||
private InstrumentedPreferenceFragment mFragment;
|
||||
@@ -217,10 +218,24 @@ public class BuildNumberPreferenceController extends BasePreferenceController im
|
||||
* @return if activity result is handled.
|
||||
*/
|
||||
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF) {
|
||||
if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF
|
||||
&& requestCode != REQUEST_IDENTITY_CHECK_FOR_DEV_PREF) {
|
||||
return false;
|
||||
}
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (requestCode == REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF
|
||||
&& resultCode == Activity.RESULT_OK) {
|
||||
final int userId = mContext.getUserId();
|
||||
if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
|
||||
false /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */,
|
||||
userId)) {
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(mFragment,
|
||||
REQUEST_IDENTITY_CHECK_FOR_DEV_PREF, userId, false /* hideBackground */);
|
||||
} else {
|
||||
enableDevelopmentSettings();
|
||||
}
|
||||
} else if (requestCode == REQUEST_IDENTITY_CHECK_FOR_DEV_PREF
|
||||
&& resultCode == Activity.RESULT_OK) {
|
||||
enableDevelopmentSettings();
|
||||
}
|
||||
mProcessingLastDevHit = false;
|
||||
|
@@ -18,13 +18,21 @@ package com.android.settings.development;
|
||||
|
||||
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.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.Flags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.provider.Settings;
|
||||
|
||||
@@ -42,6 +50,7 @@ import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
@@ -51,6 +60,7 @@ import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.shadows.ShadowBiometricManager;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@@ -61,22 +71,34 @@ import java.util.List;
|
||||
ShadowAlertDialogCompat.class,
|
||||
ShadowUserManager.class,
|
||||
ShadowUserManager.class,
|
||||
ShadowBiometricManager.class,
|
||||
})
|
||||
public class DevelopmentSettingsDashboardFragmentTest {
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
private Context mContext;
|
||||
private ShadowUserManager mShadowUserManager;
|
||||
private ShadowBiometricManager mShadowBiometricManager;
|
||||
private DevelopmentSettingsDashboardFragment mDashboard;
|
||||
private SettingsMainSwitchBar mSwitchBar;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
SettingsMainSwitchBar switchBar = new SettingsMainSwitchBar(mContext);
|
||||
mSwitchBar = new SettingsMainSwitchBar(mContext);
|
||||
mDashboard = spy(new DevelopmentSettingsDashboardFragment());
|
||||
ReflectionHelpers.setField(mDashboard, "mSwitchBar", switchBar);
|
||||
ReflectionHelpers.setField(mDashboard, "mSwitchBar", mSwitchBar);
|
||||
mShadowUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE));
|
||||
mShadowUserManager.setIsAdminUser(true);
|
||||
mShadowBiometricManager = Shadow.extract(mContext.getSystemService(
|
||||
Context.BIOMETRIC_SERVICE));
|
||||
mShadowBiometricManager.setCanAuthenticate(false);
|
||||
//TODO(b/352603684): Should be Authenticators.MANDATORY_BIOMETRICS,
|
||||
// but it is not supported by ShadowBiometricManager
|
||||
mShadowBiometricManager.setAuthenticatorType(
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -176,6 +198,41 @@ public class DevelopmentSettingsDashboardFragmentTest {
|
||||
assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
|
||||
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
|
||||
public void onSwitchChanged_turnOn_shouldLaunchBiometricPromptIfMandatoryBiometricsEffective() {
|
||||
when(mDashboard.getContext()).thenReturn(mContext);
|
||||
doNothing().when(mDashboard).startActivityForResult(any(),
|
||||
eq(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT));
|
||||
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
|
||||
mShadowBiometricManager.setCanAuthenticate(true);
|
||||
mDashboard.onCheckedChanged(null, true /* isChecked */);
|
||||
|
||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||
verify(mDashboard).startActivityForResult(any(),
|
||||
eq(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT));
|
||||
assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
|
||||
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
|
||||
public void onActivityResult_requestBiometricPrompt_shouldShowWarningDialog() {
|
||||
when(mDashboard.getContext()).thenReturn(mContext);
|
||||
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
|
||||
mDashboard.onActivityResult(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT,
|
||||
Activity.RESULT_OK, null);
|
||||
mDashboard.onCheckedChanged(null, true /* isChecked */);
|
||||
|
||||
assertThat(mSwitchBar.isChecked()).isTrue();
|
||||
assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
@Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
|
||||
|
@@ -28,8 +28,13 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.Flags;
|
||||
import android.os.Looper;
|
||||
import android.os.UserManager;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
@@ -45,6 +50,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
@@ -53,6 +59,9 @@ import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BuildNumberPreferenceControllerTest {
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule =
|
||||
DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
private static final String KEY_BUILD_NUMBER = "build_number";
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
@@ -60,6 +69,7 @@ public class BuildNumberPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private UserManager mUserManager;
|
||||
private BiometricManager mBiometricManager;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
private FakeFeatureFactory mFactory;
|
||||
@@ -76,7 +86,13 @@ public class BuildNumberPreferenceControllerTest {
|
||||
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mUserManager = (UserManager) spy(mContext.getSystemService(Context.USER_SERVICE));
|
||||
mBiometricManager = spy(mContext.getSystemService(BiometricManager.class));
|
||||
|
||||
doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
|
||||
when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
|
||||
when(mBiometricManager.canAuthenticate(mContext.getUserId(),
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
|
||||
|
||||
mFactory = FakeFeatureFactory.setupForTest();
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
@@ -156,7 +172,7 @@ public class BuildNumberPreferenceControllerTest {
|
||||
@Test
|
||||
public void onActivityResult_notConfirmPasswordRequest_doNothing() {
|
||||
final boolean activityResultHandled = mController.onActivityResult(
|
||||
BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + 1,
|
||||
BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + 2,
|
||||
Activity.RESULT_OK,
|
||||
null);
|
||||
|
||||
@@ -188,4 +204,38 @@ public class BuildNumberPreferenceControllerTest {
|
||||
assertThat(activityResultHandled).isTrue();
|
||||
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
@RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
|
||||
public void onActivityResult_confirmPasswordRequestCompleted_launchBiometricPrompt() {
|
||||
when(mUserManager.isAdminUser()).thenReturn(true);
|
||||
when(mBiometricManager.canAuthenticate(mContext.getUserId(),
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
|
||||
|
||||
final boolean activityResultHandled = mController.onActivityResult(
|
||||
BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF,
|
||||
Activity.RESULT_OK,
|
||||
null);
|
||||
|
||||
assertThat(activityResultHandled).isTrue();
|
||||
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
|
||||
verify(mFragment).startActivityForResult(any(),
|
||||
eq(BuildNumberPreferenceController.REQUEST_IDENTITY_CHECK_FOR_DEV_PREF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_confirmBiometricAuthentication_enableDevPref() {
|
||||
when(mUserManager.isAdminUser()).thenReturn(true);
|
||||
|
||||
Looper.prepare();
|
||||
final boolean activityResultHandled = mController.onActivityResult(
|
||||
BuildNumberPreferenceController.REQUEST_IDENTITY_CHECK_FOR_DEV_PREF,
|
||||
Activity.RESULT_OK,
|
||||
null);
|
||||
|
||||
assertThat(activityResultHandled).isTrue();
|
||||
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user