1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.biometrics.fingerprint;
18 
19 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
20 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_TIMEOUT;
21 import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.KEY_STATE_CANCELED;
22 
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.app.settings.SettingsEnums;
26 import android.content.Intent;
27 import android.hardware.biometrics.BiometricConstants;
28 import android.hardware.biometrics.BiometricFingerprintConstants;
29 import android.hardware.fingerprint.FingerprintManager;
30 import android.os.Bundle;
31 
32 import androidx.appcompat.app.AlertDialog;
33 import androidx.fragment.app.FragmentActivity;
34 import androidx.fragment.app.FragmentManager;
35 
36 import com.android.settings.R;
37 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
38 
39 /** Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment. */
40 public class FingerprintErrorDialog extends InstrumentedDialogFragment {
41 
42     public static final String KEY_ERROR_MSG = "error_msg";
43     public static final String KEY_ERROR_TITLE = "error_title";
44     public static final String KEY_ERROR_ID = "error_id";
45     public static final String KEY_UDFPS = "is_udfps";
46 
47     @Override
onCreateDialog(Bundle savedInstanceState)48     public Dialog onCreateDialog(Bundle savedInstanceState) {
49         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
50         final CharSequence errorString = getArguments().getCharSequence(KEY_ERROR_MSG);
51         final CharSequence errorTitle = getArguments().getCharSequence(KEY_ERROR_TITLE);
52         final int errMsgId = getArguments().getInt(KEY_ERROR_ID);
53         final boolean wasTimeout = errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
54         final boolean showTryAgain = errMsgId
55                 == BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS;
56 
57         builder.setTitle(errorTitle)
58                 .setMessage(errorString)
59                 .setCancelable(false)
60                 .setPositiveButton(
61                         R.string.security_settings_fingerprint_enroll_dialog_ok,
62                         (dialog, which) -> {
63                             dialog.dismiss();
64                             final Activity activity = getActivity();
65                             if (wasTimeout) {
66                                 activity.setResult(RESULT_TIMEOUT);
67                             } else {
68                                 activity.setResult(RESULT_FINISHED);
69                             }
70                             activity.finish();
71                         });
72 
73         if (showTryAgain) {
74             builder.setPositiveButton(
75                     R.string.security_settings_fingerprint_enroll_dialog_try_again,
76                     (dialog, which) -> {
77                         dialog.dismiss();
78                         final Activity activity = getActivity();
79                         final Intent intent = activity.getIntent();
80                         intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
81                         intent.putExtra(KEY_STATE_CANCELED, false);
82                         activity.startActivity(intent);
83                         activity.finish();
84                     })
85                     .setNegativeButton(R.string.security_settings_fingerprint_enroll_dialog_ok,
86                             (dialog, which) -> {
87                                 dialog.dismiss();
88                                 final Activity activity = getActivity();
89                                 activity.setResult(RESULT_FINISHED);
90                                 activity.finish();
91                             });
92         }
93         final AlertDialog dialog = builder.create();
94         dialog.setCanceledOnTouchOutside(false);
95         return dialog;
96     }
97 
showErrorDialog(FragmentActivity host, int errMsgId, boolean isSetup)98     public static void showErrorDialog(FragmentActivity host, int errMsgId, boolean isSetup) {
99         if (host.isFinishing()) {
100             return;
101         }
102         final FragmentManager fragmentManager = host.getSupportFragmentManager();
103         if (fragmentManager.isDestroyed() || fragmentManager.isStateSaved()) {
104             return;
105         }
106         CharSequence errMsg;
107         if (isSetup) {
108             errMsg = host.getText(getSetupErrorMessage(errMsgId));
109         } else {
110             errMsg = host.getText(getErrorMessage(errMsgId));
111         }
112         final CharSequence errTitle = host.getText(getErrorTitle(errMsgId));
113         final FingerprintErrorDialog dialog = newInstance(errMsg, errTitle, errMsgId);
114         dialog.show(fragmentManager, FingerprintErrorDialog.class.getName());
115     }
116 
117     /**
118      * Gets dialog message as error id inside {@link FingerprintManager}
119      */
getSetupErrorMessage(int errMsgId)120     public static int getSetupErrorMessage(int errMsgId) {
121         switch (errMsgId) {
122             case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
123                 // This message happens when the underlying crypto layer decides to revoke
124                 // the enrollment auth token.
125                 return R.string
126                         .security_settings_fingerprint_enroll_error_timeout_dialog_message_setup;
127             case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBRATION:
128                 return R.string.security_settings_fingerprint_bad_calibration;
129             case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
130                 return R.string
131                         .security_settings_fingerprint_enroll_error_unable_to_process_message_setup;
132             default:
133                 // There's nothing specific to tell the user about. Ask them to try again.
134                 return R.string
135                         .security_settings_fingerprint_enroll_error_generic_dialog_message_setup;
136         }
137     }
138 
139     /**
140      * Gets dialog message as error id inside {@link FingerprintManager}
141      */
getErrorMessage(int errMsgId)142     public static int getErrorMessage(int errMsgId) {
143         switch (errMsgId) {
144             case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
145                 // This message happens when the underlying crypto layer decides to revoke
146                 // the enrollment auth token.
147                 return R.string.security_settings_fingerprint_enroll_error_timeout_dialog_message;
148             case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBRATION:
149                 return R.string.security_settings_fingerprint_bad_calibration;
150             case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
151                 return R.string
152                         .security_settings_fingerprint_enroll_error_unable_to_process_message;
153             default:
154                 // There's nothing specific to tell the user about. Ask them to try again.
155                 return R.string.security_settings_fingerprint_enroll_error_generic_dialog_message;
156         }
157     }
158 
159     /**
160      * Gets dialog title as error id inside {@link FingerprintManager}
161      */
getErrorTitle(int errMsgId)162     public static int getErrorTitle(int errMsgId) {
163         switch (errMsgId) {
164             case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
165                 return R.string.security_settings_fingerprint_enroll_error_dialog_title;
166             case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBRATION:
167                 return R.string.security_settings_fingerprint_bad_calibration_title;
168             default:
169                 return R.string
170                         .security_settings_fingerprint_enroll_error_unable_to_process_dialog_title;
171         }
172     }
173 
newInstance(CharSequence msg, CharSequence title, int msgId)174     private static FingerprintErrorDialog newInstance(CharSequence msg, CharSequence title,
175             int msgId) {
176         final FingerprintErrorDialog dialog = new FingerprintErrorDialog();
177         final Bundle args = new Bundle();
178         args.putCharSequence(KEY_ERROR_MSG, msg);
179         args.putCharSequence(KEY_ERROR_TITLE, title);
180         args.putInt(KEY_ERROR_ID, msgId);
181         dialog.setArguments(args);
182         return dialog;
183     }
184 
185     @Override
getMetricsCategory()186     public int getMetricsCategory() {
187         return SettingsEnums.DIALOG_FINGERPINT_ERROR;
188     }
189 }
190