Merge "Add mandatory biometric prompt to platform surfaces (4/N)" into main

This commit is contained in:
Diya Bera
2024-07-31 17:06:14 +00:00
committed by Android (Google) Code Review
4 changed files with 149 additions and 6 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

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