1 /*
2  * Copyright (C) 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.server.biometrics.sensors;
18 
19 import android.annotation.NonNull;
20 import android.app.Notification;
21 import android.app.NotificationChannel;
22 import android.app.NotificationManager;
23 import android.app.PendingIntent;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.os.SystemClock;
27 import android.os.UserHandle;
28 import android.util.Slog;
29 
30 import com.android.internal.R;
31 
32 /**
33  * Biometric notification helper class.
34  */
35 public class BiometricNotificationUtils {
36 
37     private static final String TAG = "BiometricNotificationUtils";
38     private static final String FACE_RE_ENROLL_NOTIFICATION_TAG = "FaceReEnroll";
39     private static final String FACE_ENROLL_NOTIFICATION_TAG = "FaceEnroll";
40     private static final String FINGERPRINT_ENROLL_NOTIFICATION_TAG = "FingerprintEnroll";
41     private static final String BAD_CALIBRATION_NOTIFICATION_TAG = "FingerprintBadCalibration";
42     private static final String KEY_RE_ENROLL_FACE = "re_enroll_face_unlock";
43     private static final String FACE_SETTINGS_ACTION = "android.settings.FACE_SETTINGS";
44     private static final String FINGERPRINT_SETTINGS_ACTION =
45             "android.settings.FINGERPRINT_SETTINGS";
46     private static final String FACE_ENROLL_ACTION = "android.settings.FACE_ENROLL";
47     private static final String FINGERPRINT_ENROLL_ACTION = "android.settings.FINGERPRINT_ENROLL";
48     private static final String SETTINGS_PACKAGE = "com.android.settings";
49     private static final String FACE_ENROLL_CHANNEL = "FaceEnrollNotificationChannel";
50     private static final String FACE_RE_ENROLL_CHANNEL = "FaceReEnrollNotificationChannel";
51     private static final String FINGERPRINT_ENROLL_CHANNEL = "FingerprintEnrollNotificationChannel";
52     private static final String FINGERPRINT_BAD_CALIBRATION_CHANNEL =
53             "FingerprintBadCalibrationNotificationChannel";
54     private static final int NOTIFICATION_ID = 1;
55     private static final long NOTIFICATION_INTERVAL_MS = 24 * 60 * 60 * 1000;
56     private static long sLastAlertTime = 0;
57 
58     /**
59      * Shows a face re-enrollment notification.
60      */
showReEnrollmentNotification(@onNull Context context)61     public static void showReEnrollmentNotification(@NonNull Context context) {
62         final NotificationManager notificationManager =
63                 context.getSystemService(NotificationManager.class);
64 
65         final String name =
66                 context.getString(R.string.face_recalibrate_notification_name);
67         final String title =
68                 context.getString(R.string.face_recalibrate_notification_title);
69         final String content =
70                 context.getString(R.string.face_recalibrate_notification_content);
71 
72         final Intent intent = new Intent(FACE_SETTINGS_ACTION);
73         intent.setPackage(SETTINGS_PACKAGE);
74         intent.putExtra(KEY_RE_ENROLL_FACE, true);
75 
76         final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
77                 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
78                 null /* options */, UserHandle.CURRENT);
79 
80         showNotificationHelper(context, name, title, content, pendingIntent, FACE_RE_ENROLL_CHANNEL,
81                 Notification.CATEGORY_SYSTEM, FACE_RE_ENROLL_NOTIFICATION_TAG,
82                 Notification.VISIBILITY_SECRET);
83     }
84 
85     /**
86      * Shows a face enrollment notification.
87      */
showFaceEnrollNotification(@onNull Context context)88     public static void showFaceEnrollNotification(@NonNull Context context) {
89 
90         final String name =
91                 context.getString(R.string.device_unlock_notification_name);
92         final String title =
93                 context.getString(R.string.alternative_unlock_setup_notification_title);
94         final String content =
95                 context.getString(R.string.alternative_face_setup_notification_content);
96 
97         final Intent intent = new Intent(FACE_ENROLL_ACTION);
98         intent.setPackage(SETTINGS_PACKAGE);
99 
100         final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
101                 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
102                 null /* options */, UserHandle.CURRENT);
103 
104         showNotificationHelper(context, name, title, content, pendingIntent, FACE_ENROLL_CHANNEL,
105                 Notification.CATEGORY_RECOMMENDATION, FACE_ENROLL_NOTIFICATION_TAG,
106                 Notification.VISIBILITY_PUBLIC);
107     }
108 
109     /**
110      * Shows a fingerprint enrollment notification.
111      */
showFingerprintEnrollNotification(@onNull Context context)112     public static void showFingerprintEnrollNotification(@NonNull Context context) {
113 
114         final String name =
115                 context.getString(R.string.device_unlock_notification_name);
116         final String title =
117                 context.getString(R.string.alternative_unlock_setup_notification_title);
118         final String content =
119                 context.getString(R.string.alternative_fp_setup_notification_content);
120 
121         final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
122         intent.setPackage(SETTINGS_PACKAGE);
123 
124         final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
125                 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
126                 null /* options */, UserHandle.CURRENT);
127 
128         showNotificationHelper(context, name, title, content, pendingIntent,
129                 Notification.CATEGORY_RECOMMENDATION, FINGERPRINT_ENROLL_CHANNEL,
130                 FINGERPRINT_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC);
131     }
132 
133     /**
134      * Shows a fingerprint bad calibration notification.
135      */
showBadCalibrationNotification(@onNull Context context)136     public static void showBadCalibrationNotification(@NonNull Context context) {
137         final long currentTime = SystemClock.elapsedRealtime();
138         final long timeSinceLastAlert = currentTime - sLastAlertTime;
139 
140         // Only show the notification if not previously shown or a day has
141         // passed since the last notification.
142         if (sLastAlertTime != 0 && (timeSinceLastAlert < NOTIFICATION_INTERVAL_MS)) {
143             Slog.v(TAG, "Skipping calibration notification : " + timeSinceLastAlert);
144             return;
145         }
146 
147         sLastAlertTime = currentTime;
148 
149         final String name =
150                 context.getString(R.string.fingerprint_recalibrate_notification_name);
151         final String title =
152                 context.getString(R.string.fingerprint_recalibrate_notification_title);
153         final String content =
154                 context.getString(R.string.fingerprint_recalibrate_notification_content);
155 
156         final Intent intent = new Intent(FINGERPRINT_SETTINGS_ACTION);
157         intent.setPackage(SETTINGS_PACKAGE);
158 
159         final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
160                 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
161                 null /* options */, UserHandle.CURRENT);
162 
163         showNotificationHelper(context, name, title, content, pendingIntent,
164                 Notification.CATEGORY_SYSTEM, FINGERPRINT_BAD_CALIBRATION_CHANNEL,
165                 BAD_CALIBRATION_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET);
166     }
167 
showNotificationHelper(Context context, String name, String title, String content, PendingIntent pendingIntent, String category, String channelName, String notificationTag, int visibility)168     private static void showNotificationHelper(Context context, String name, String title,
169                 String content, PendingIntent pendingIntent, String category,
170                 String channelName, String notificationTag, int visibility) {
171         final NotificationManager notificationManager =
172                 context.getSystemService(NotificationManager.class);
173         final NotificationChannel channel = new NotificationChannel(channelName, name,
174                 NotificationManager.IMPORTANCE_HIGH);
175         final Notification notification = new Notification.Builder(context, channelName)
176                 .setSmallIcon(R.drawable.ic_lock)
177                 .setContentTitle(title)
178                 .setContentText(content)
179                 .setStyle(new Notification.BigTextStyle().bigText(content))
180                 .setSubText(name)
181                 .setOnlyAlertOnce(true)
182                 .setLocalOnly(true)
183                 .setAutoCancel(true)
184                 .setCategory(category)
185                 .setContentIntent(pendingIntent)
186                 .setVisibility(visibility)
187                 .build();
188 
189         notificationManager.createNotificationChannel(channel);
190         notificationManager.notifyAsUser(notificationTag, NOTIFICATION_ID, notification,
191                 UserHandle.CURRENT);
192     }
193 
194     /**
195      * Cancels a face re-enrollment notification
196      */
cancelFaceReEnrollNotification(@onNull Context context)197     public static void cancelFaceReEnrollNotification(@NonNull Context context) {
198         final NotificationManager notificationManager =
199                 context.getSystemService(NotificationManager.class);
200         notificationManager.cancelAsUser(FACE_RE_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID,
201                 UserHandle.CURRENT);
202     }
203 
204     /**
205      * Cancels a face enrollment notification
206      */
cancelFaceEnrollNotification(@onNull Context context)207     public static void cancelFaceEnrollNotification(@NonNull Context context) {
208         final NotificationManager notificationManager =
209                 context.getSystemService(NotificationManager.class);
210         notificationManager.cancelAsUser(FACE_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID,
211                 UserHandle.CURRENT);
212     }
213 
214     /**
215      * Cancels a fingerprint enrollment notification
216      */
cancelFingerprintEnrollNotification(@onNull Context context)217     public static void cancelFingerprintEnrollNotification(@NonNull Context context) {
218         final NotificationManager notificationManager =
219                 context.getSystemService(NotificationManager.class);
220         notificationManager.cancelAsUser(FINGERPRINT_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID,
221                 UserHandle.CURRENT);
222     }
223 
224     /**
225      * Cancels a fingerprint bad calibration notification
226      */
cancelBadCalibrationNotification(@onNull Context context)227     public static void cancelBadCalibrationNotification(@NonNull Context context) {
228         final NotificationManager notificationManager =
229                 context.getSystemService(NotificationManager.class);
230         notificationManager.cancelAsUser(BAD_CALIBRATION_NOTIFICATION_TAG, NOTIFICATION_ID,
231                 UserHandle.CURRENT);
232     }
233 
234 }
235