Flag: android.hardware.biometrics.flag.mandatory_biometrics Bug: 358641110 Bug: 358179610 Test: Manual Change-Id: I8f9cc16a0ffc43655534663203ff4da6380fbc97
258 lines
11 KiB
Java
258 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2024 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.biometrics;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.app.AlertDialog;
|
|
import android.app.Dialog;
|
|
import android.app.admin.DevicePolicyManager;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.os.Bundle;
|
|
import android.provider.Settings;
|
|
import android.text.SpannableString;
|
|
import android.text.Spanned;
|
|
import android.text.TextPaint;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.text.style.ClickableSpan;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.fragment.app.FragmentActivity;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
|
|
|
/** Initializes and shows biometric error dialogs related to identity check. */
|
|
public class IdentityCheckBiometricErrorDialog extends InstrumentedDialogFragment {
|
|
private static final String TAG = "BiometricErrorDialog";
|
|
|
|
private static final String KEY_ERROR_CODE = "key_error_code";
|
|
private static final String KEY_TWO_FACTOR_AUTHENTICATION = "key_two_factor_authentication";
|
|
private static final String KEY_FINISH_ACTIVITY = "key_finish_activity";
|
|
|
|
private String mActionIdentityCheckSettings = Settings.ACTION_SETTINGS;
|
|
private String mIdentityCheckSettingsPackageName;
|
|
@Nullable private BroadcastReceiver mBroadcastReceiver;
|
|
private boolean mShouldFinishActivity = false;
|
|
|
|
@NonNull
|
|
@Override
|
|
public Dialog onCreateDialog(
|
|
@Nullable Bundle savedInstanceState) {
|
|
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
|
|
final LayoutInflater inflater = getActivity().getLayoutInflater();
|
|
final boolean isLockoutError = getArguments().getString(KEY_ERROR_CODE).equals(
|
|
Utils.BiometricStatus.LOCKOUT.name());
|
|
final View customView = inflater.inflate(R.layout.biometric_lockout_error_dialog,
|
|
null);
|
|
final boolean twoFactorAuthentication = getArguments().getBoolean(
|
|
KEY_TWO_FACTOR_AUTHENTICATION);
|
|
final String identityCheckSettingsAction = getActivity().getString(
|
|
com.android.internal.R.string.identity_check_settings_action);
|
|
mActionIdentityCheckSettings = identityCheckSettingsAction.isEmpty()
|
|
? mActionIdentityCheckSettings : identityCheckSettingsAction;
|
|
mIdentityCheckSettingsPackageName = getActivity().getString(
|
|
com.android.internal.R.string.identity_check_settings_package_name);
|
|
mShouldFinishActivity = getArguments().getBoolean(
|
|
KEY_FINISH_ACTIVITY);
|
|
|
|
setTitle(customView, isLockoutError);
|
|
setBody(customView, isLockoutError, twoFactorAuthentication);
|
|
alertDialogBuilder.setView(customView);
|
|
setPositiveButton(alertDialogBuilder, isLockoutError, twoFactorAuthentication);
|
|
if (!isLockoutError || !twoFactorAuthentication) {
|
|
setNegativeButton(alertDialogBuilder, isLockoutError);
|
|
}
|
|
if (isLockoutError) {
|
|
mBroadcastReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
|
|
dismiss();
|
|
}
|
|
}
|
|
};
|
|
getContext().registerReceiver(mBroadcastReceiver,
|
|
new IntentFilter(Intent.ACTION_SCREEN_OFF));
|
|
}
|
|
|
|
return alertDialogBuilder.create();
|
|
}
|
|
|
|
@Override
|
|
public void onDestroyView() {
|
|
super.onDestroyView();
|
|
if (mBroadcastReceiver != null) {
|
|
getContext().unregisterReceiver(mBroadcastReceiver);
|
|
mBroadcastReceiver = null;
|
|
}
|
|
if (mShouldFinishActivity && getActivity() != null) {
|
|
getActivity().finish();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows an error dialog to prompt the user to resolve biometric errors for identity check.
|
|
* @param fragmentActivity calling activity
|
|
* @param errorCode refers to the biometric error
|
|
* @param twoFactorAuthentication if the surface requests LSKF before identity check auth
|
|
*/
|
|
public static void showBiometricErrorDialog(FragmentActivity fragmentActivity,
|
|
Utils.BiometricStatus errorCode, boolean twoFactorAuthentication) {
|
|
showBiometricErrorDialog(fragmentActivity, errorCode, twoFactorAuthentication,
|
|
false /* finishActivityOnDismiss */);
|
|
}
|
|
|
|
/**
|
|
* Shows an error dialog to prompt the user to resolve biometric errors for identity check.
|
|
* Finishes the activity once the dialog is dismissed.
|
|
* @param fragmentActivity calling activity
|
|
* @param errorCode refers to the biometric error
|
|
*/
|
|
public static void showBiometricErrorDialogAndFinishActivityOnDismiss(
|
|
FragmentActivity fragmentActivity, Utils.BiometricStatus errorCode) {
|
|
showBiometricErrorDialog(fragmentActivity, errorCode, true /* twoFactorAuthentication */,
|
|
true /* finishActivityOnDismiss */);
|
|
}
|
|
|
|
private static void showBiometricErrorDialog(FragmentActivity fragmentActivity,
|
|
Utils.BiometricStatus errorCode, boolean twoFactorAuthentication,
|
|
boolean finishActivityOnDismiss) {
|
|
final IdentityCheckBiometricErrorDialog identityCheckBiometricErrorDialog =
|
|
new IdentityCheckBiometricErrorDialog();
|
|
final Bundle args = new Bundle();
|
|
args.putCharSequence(KEY_ERROR_CODE, errorCode.name());
|
|
args.putBoolean(KEY_TWO_FACTOR_AUTHENTICATION, twoFactorAuthentication);
|
|
args.putBoolean(KEY_FINISH_ACTIVITY, finishActivityOnDismiss);
|
|
identityCheckBiometricErrorDialog.setArguments(args);
|
|
identityCheckBiometricErrorDialog.show(fragmentActivity.getSupportFragmentManager(),
|
|
IdentityCheckBiometricErrorDialog.class.getName());
|
|
}
|
|
private void setTitle(View view, boolean lockout) {
|
|
final TextView titleTextView = view.findViewById(R.id.title);
|
|
if (lockout) {
|
|
titleTextView.setText(R.string.identity_check_lockout_error_title);
|
|
} else {
|
|
titleTextView.setText(R.string.identity_check_general_error_title);
|
|
}
|
|
}
|
|
|
|
private void setBody(View view, boolean lockout, boolean twoFactorAuthentication) {
|
|
final TextView textView1 = view.findViewById(R.id.description_1);
|
|
final TextView textView2 = view.findViewById(R.id.description_2);
|
|
|
|
if (lockout) {
|
|
if (twoFactorAuthentication) {
|
|
textView1.setText(
|
|
R.string.identity_check_lockout_error_two_factor_auth_description_1);
|
|
} else {
|
|
textView1.setText(R.string.identity_check_lockout_error_description_1);
|
|
}
|
|
textView2.setText(getClickableDescriptionForLockoutError());
|
|
textView2.setMovementMethod(LinkMovementMethod.getInstance());
|
|
} else {
|
|
textView1.setText(R.string.identity_check_general_error_description_1);
|
|
textView2.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
|
|
private SpannableString getClickableDescriptionForLockoutError() {
|
|
final String description = getResources().getString(
|
|
R.string.identity_check_lockout_error_description_2);
|
|
final SpannableString spannableString = new SpannableString(description);
|
|
final ClickableSpan clickableSpan = new ClickableSpan() {
|
|
@Override
|
|
public void onClick(View textView) {
|
|
dismiss();
|
|
final Intent autoLockSettingsIntent = new Intent(mActionIdentityCheckSettings);
|
|
final ResolveInfo autoLockSettingsInfo = getActivity().getPackageManager()
|
|
.resolveActivity(autoLockSettingsIntent, 0 /* flags */);
|
|
if (autoLockSettingsInfo != null) {
|
|
startActivity(autoLockSettingsIntent);
|
|
} else {
|
|
Log.e(TAG, "Auto lock settings intent could not be resolved.");
|
|
}
|
|
}
|
|
@Override
|
|
public void updateDrawState(TextPaint ds) {
|
|
super.updateDrawState(ds);
|
|
ds.setUnderlineText(true);
|
|
}
|
|
};
|
|
final String goToSettings = getActivity().getString(R.string.go_to_settings);
|
|
spannableString.setSpan(clickableSpan, description.indexOf(goToSettings),
|
|
description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
|
return spannableString;
|
|
}
|
|
|
|
private void setPositiveButton(AlertDialog.Builder alertDialogBuilder, boolean lockout,
|
|
boolean twoFactorAuthentication) {
|
|
if (lockout) {
|
|
if (twoFactorAuthentication) {
|
|
alertDialogBuilder.setPositiveButton((R.string.okay),
|
|
(dialog, which) -> dialog.dismiss());
|
|
} else {
|
|
DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
|
|
getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
|
|
alertDialogBuilder.setPositiveButton(
|
|
R.string.identity_check_lockout_error_lock_screen,
|
|
(dialog, which) -> {
|
|
dialog.dismiss();
|
|
devicePolicyManager.lockNow();
|
|
});
|
|
}
|
|
} else {
|
|
alertDialogBuilder.setPositiveButton(R.string.identity_check_biometric_error_ok,
|
|
(dialog, which) -> dialog.dismiss());
|
|
}
|
|
}
|
|
|
|
private void setNegativeButton(AlertDialog.Builder alertDialogBuilder, boolean lockout) {
|
|
if (lockout) {
|
|
alertDialogBuilder.setNegativeButton(R.string.identity_check_biometric_error_cancel,
|
|
(dialog, which) -> dialog.dismiss());
|
|
} else {
|
|
alertDialogBuilder.setNegativeButton(R.string.go_to_identity_check,
|
|
(dialog, which) -> {
|
|
final Intent autoLockSettingsIntent = new Intent(
|
|
mActionIdentityCheckSettings);
|
|
final ResolveInfo autoLockSettingsInfo = getActivity().getPackageManager()
|
|
.resolveActivity(autoLockSettingsIntent, 0 /* flags */);
|
|
if (autoLockSettingsInfo != null) {
|
|
startActivity(autoLockSettingsIntent);
|
|
} else {
|
|
Log.e(TAG, "Identity check settings intent could not be resolved.");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return 0;
|
|
}
|
|
}
|