1 /*
2  * Copyright (C) 2023 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.systemui.biometrics;
18 
19 import android.app.Dialog;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.hardware.biometrics.BiometricSourceType;
23 import android.hardware.face.Face;
24 import android.hardware.face.FaceManager;
25 import android.hardware.fingerprint.Fingerprint;
26 import android.hardware.fingerprint.FingerprintManager;
27 import android.provider.Settings;
28 import android.util.Log;
29 
30 import com.android.systemui.R;
31 import com.android.systemui.dagger.SysUISingleton;
32 import com.android.systemui.statusbar.phone.SystemUIDialog;
33 
34 import javax.inject.Inject;
35 
36 /**
37  * Manages the creation of dialogs to be shown for biometric re enroll notifications.
38  */
39 @SysUISingleton
40 public class BiometricNotificationDialogFactory {
41     private static final String TAG = "BiometricNotificationDialogFactory";
42 
43     @Inject
BiometricNotificationDialogFactory()44     BiometricNotificationDialogFactory() {}
45 
createReenrollDialog(final Context context, final SystemUIDialog sysuiDialog, BiometricSourceType biometricSourceType)46     Dialog createReenrollDialog(final Context context, final SystemUIDialog sysuiDialog,
47             BiometricSourceType biometricSourceType) {
48         if (biometricSourceType == BiometricSourceType.FACE) {
49             sysuiDialog.setTitle(context.getString(R.string.face_re_enroll_dialog_title));
50             sysuiDialog.setMessage(context.getString(R.string.face_re_enroll_dialog_content));
51         } else if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
52             FingerprintManager fingerprintManager = context.getSystemService(
53                     FingerprintManager.class);
54             sysuiDialog.setTitle(context.getString(R.string.fingerprint_re_enroll_dialog_title));
55             if (fingerprintManager.getEnrolledFingerprints().size() == 1) {
56                 sysuiDialog.setMessage(context.getString(
57                         R.string.fingerprint_re_enroll_dialog_content_singular));
58             } else {
59                 sysuiDialog.setMessage(context.getString(
60                         R.string.fingerprint_re_enroll_dialog_content));
61             }
62         }
63 
64         sysuiDialog.setPositiveButton(R.string.biometric_re_enroll_dialog_confirm,
65                 (dialog, which) -> onReenrollDialogConfirm(context, biometricSourceType));
66         sysuiDialog.setNegativeButton(R.string.biometric_re_enroll_dialog_cancel,
67                 (dialog, which) -> {});
68         return sysuiDialog;
69     }
70 
createReenrollFailureDialog(Context context, BiometricSourceType biometricType)71     private static Dialog createReenrollFailureDialog(Context context,
72             BiometricSourceType biometricType) {
73         final SystemUIDialog sysuiDialog = new SystemUIDialog(context);
74 
75         if (biometricType == BiometricSourceType.FACE) {
76             sysuiDialog.setMessage(context.getString(
77                     R.string.face_reenroll_failure_dialog_content));
78         } else if (biometricType == BiometricSourceType.FINGERPRINT) {
79             sysuiDialog.setMessage(context.getString(
80                     R.string.fingerprint_reenroll_failure_dialog_content));
81         }
82 
83         sysuiDialog.setPositiveButton(R.string.ok, (dialog, which) -> {});
84         return sysuiDialog;
85     }
86 
onReenrollDialogConfirm(final Context context, BiometricSourceType biometricType)87     private static void onReenrollDialogConfirm(final Context context,
88             BiometricSourceType biometricType) {
89         if (biometricType == BiometricSourceType.FACE) {
90             reenrollFace(context);
91         } else if (biometricType == BiometricSourceType.FINGERPRINT) {
92             reenrollFingerprint(context);
93         }
94     }
95 
reenrollFingerprint(Context context)96     private static void reenrollFingerprint(Context context) {
97         FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
98         if (fingerprintManager == null) {
99             Log.e(TAG, "Not launching enrollment. Fingerprint manager was null!");
100             createReenrollFailureDialog(context, BiometricSourceType.FINGERPRINT).show();
101             return;
102         }
103 
104         if (!fingerprintManager.hasEnrolledTemplates(context.getUserId())) {
105             createReenrollFailureDialog(context, BiometricSourceType.FINGERPRINT).show();
106             return;
107         }
108 
109         // Remove all enrolled fingerprint. Launch enrollment if successful.
110         fingerprintManager.removeAll(context.getUserId(),
111                 new FingerprintManager.RemovalCallback() {
112                     boolean mDidShowFailureDialog;
113 
114                     @Override
115                     public void onRemovalError(Fingerprint fingerprint, int errMsgId,
116                             CharSequence errString) {
117                         Log.e(TAG, "Not launching enrollment."
118                                 + "Failed to remove existing face(s).");
119                         if (!mDidShowFailureDialog) {
120                             mDidShowFailureDialog = true;
121                             createReenrollFailureDialog(context, BiometricSourceType.FINGERPRINT)
122                                     .show();
123                         }
124                     }
125 
126                     @Override
127                     public void onRemovalSucceeded(Fingerprint fingerprint, int remaining) {
128                         if (!mDidShowFailureDialog && remaining == 0) {
129                             Intent intent = new Intent(Settings.ACTION_FINGERPRINT_ENROLL);
130                             intent.setPackage("com.android.settings");
131                             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
132                             context.startActivity(intent);
133                         }
134                     }
135                 });
136     }
137 
reenrollFace(Context context)138     private static void reenrollFace(Context context) {
139         FaceManager faceManager = context.getSystemService(FaceManager.class);
140         if (faceManager == null) {
141             Log.e(TAG, "Not launching enrollment. Face manager was null!");
142             createReenrollFailureDialog(context, BiometricSourceType.FACE).show();
143             return;
144         }
145 
146         if (!faceManager.hasEnrolledTemplates(context.getUserId())) {
147             createReenrollFailureDialog(context, BiometricSourceType.FACE).show();
148             return;
149         }
150 
151         // Remove all enrolled faces. Launch enrollment if successful.
152         faceManager.removeAll(context.getUserId(),
153                 new FaceManager.RemovalCallback() {
154                     boolean mDidShowFailureDialog;
155 
156                     @Override
157                     public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
158                         Log.e(TAG, "Not launching enrollment."
159                                 + "Failed to remove existing face(s).");
160                         if (!mDidShowFailureDialog) {
161                             mDidShowFailureDialog = true;
162                             createReenrollFailureDialog(context, BiometricSourceType.FACE).show();
163                         }
164                     }
165 
166                     @Override
167                     public void onRemovalSucceeded(Face face, int remaining) {
168                         if (!mDidShowFailureDialog && remaining == 0) {
169                             Intent intent = new Intent("android.settings.FACE_ENROLL");
170                             intent.setPackage("com.android.settings");
171                             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
172                             context.startActivity(intent);
173                         }
174                     }
175                 });
176     }
177 }
178