blob: 6e62b0d575a25de96b725b13e5c192b91461a654 [file] [log] [blame]
Kevin Chynaae4a152018-01-18 11:48:09 -08001/*
2 * Copyright (C) 2018 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
Kevin Chyne9275662018-07-23 16:42:06 -070017package com.android.systemui.biometrics;
Kevin Chynaae4a152018-01-18 11:48:09 -080018
Kevin Chyn42653e82018-01-19 14:15:46 -080019import android.content.Context;
Kevin Chynaae4a152018-01-18 11:48:09 -080020import android.content.pm.PackageManager;
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070021import android.hardware.biometrics.BiometricPrompt;
22import android.hardware.biometrics.IBiometricPromptReceiver;
Kevin Chynaae4a152018-01-18 11:48:09 -080023import android.os.Bundle;
Kevin Chyn42653e82018-01-19 14:15:46 -080024import android.os.Handler;
25import android.os.Message;
26import android.os.RemoteException;
Kevin Chynaae4a152018-01-18 11:48:09 -080027import android.util.Log;
Kevin Chyn42653e82018-01-19 14:15:46 -080028import android.view.WindowManager;
Kevin Chynaae4a152018-01-18 11:48:09 -080029
Kevin Chyn42653e82018-01-19 14:15:46 -080030import com.android.internal.os.SomeArgs;
Kevin Chynaae4a152018-01-18 11:48:09 -080031import com.android.systemui.SystemUI;
32import com.android.systemui.statusbar.CommandQueue;
33
Kevin Chyne9275662018-07-23 16:42:06 -070034/**
35 * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
36 * FingerprintDialogView).
37 */
38public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
Kevin Chynaae4a152018-01-18 11:48:09 -080039 private static final String TAG = "FingerprintDialogImpl";
Kevin Chyn42653e82018-01-19 14:15:46 -080040 private static final boolean DEBUG = true;
41
Kevin Chyn5906c172018-07-23 15:43:02 -070042 private static final int MSG_SHOW_DIALOG = 1;
Kevin Chyne9275662018-07-23 16:42:06 -070043 private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
44 private static final int MSG_BIOMETRIC_HELP = 3;
45 private static final int MSG_BIOMETRIC_ERROR = 4;
46 private static final int MSG_HIDE_DIALOG = 5;
Kevin Chyn5906c172018-07-23 15:43:02 -070047 private static final int MSG_BUTTON_NEGATIVE = 6;
48 private static final int MSG_USER_CANCELED = 7;
49 private static final int MSG_BUTTON_POSITIVE = 8;
Kevin Chyn42653e82018-01-19 14:15:46 -080050
51 private FingerprintDialogView mDialogView;
52 private WindowManager mWindowManager;
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070053 private IBiometricPromptReceiver mReceiver;
Kevin Chyn42653e82018-01-19 14:15:46 -080054 private boolean mDialogShowing;
Kevin Chyn5906c172018-07-23 15:43:02 -070055 private Callback mCallback = new Callback();
Kevin Chyn42653e82018-01-19 14:15:46 -080056
57 private Handler mHandler = new Handler() {
58 @Override
59 public void handleMessage(Message msg) {
60 switch(msg.what) {
61 case MSG_SHOW_DIALOG:
62 handleShowDialog((SomeArgs) msg.obj);
63 break;
Kevin Chyne9275662018-07-23 16:42:06 -070064 case MSG_BIOMETRIC_AUTHENTICATED:
65 handleBiometricAuthenticated();
Kevin Chyn42653e82018-01-19 14:15:46 -080066 break;
Kevin Chyne9275662018-07-23 16:42:06 -070067 case MSG_BIOMETRIC_HELP:
68 handleBiometricHelp((String) msg.obj);
Kevin Chyn42653e82018-01-19 14:15:46 -080069 break;
Kevin Chyne9275662018-07-23 16:42:06 -070070 case MSG_BIOMETRIC_ERROR:
71 handleBiometricError((String) msg.obj);
Kevin Chyn42653e82018-01-19 14:15:46 -080072 break;
73 case MSG_HIDE_DIALOG:
74 handleHideDialog((Boolean) msg.obj);
75 break;
76 case MSG_BUTTON_NEGATIVE:
77 handleButtonNegative();
78 break;
79 case MSG_USER_CANCELED:
80 handleUserCanceled();
81 break;
82 case MSG_BUTTON_POSITIVE:
83 handleButtonPositive();
84 break;
Kevin Chyn42653e82018-01-19 14:15:46 -080085 }
86 }
87 };
Kevin Chynaae4a152018-01-18 11:48:09 -080088
Kevin Chyn5906c172018-07-23 15:43:02 -070089 private class Callback implements DialogViewCallback {
90 @Override
91 public void onUserCanceled() {
Kevin Chyne9275662018-07-23 16:42:06 -070092 mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
Kevin Chyn5906c172018-07-23 15:43:02 -070093 }
94
95 @Override
96 public void onErrorShown() {
97 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
98 false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
99 }
100
101 @Override
102 public void onNegativePressed() {
Kevin Chyne9275662018-07-23 16:42:06 -0700103 mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
Kevin Chyn5906c172018-07-23 15:43:02 -0700104 }
105
106 @Override
107 public void onPositivePressed() {
Kevin Chyne9275662018-07-23 16:42:06 -0700108 mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
Kevin Chyn5906c172018-07-23 15:43:02 -0700109 }
110 }
111
Kevin Chynaae4a152018-01-18 11:48:09 -0800112 @Override
113 public void start() {
114 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
115 return;
116 }
117 getComponent(CommandQueue.class).addCallbacks(this);
Kevin Chyne8f3e1b2018-01-23 17:33:58 -0800118 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Kevin Chyn5906c172018-07-23 15:43:02 -0700119 mDialogView = new FingerprintDialogView(mContext, mCallback);
Kevin Chynaae4a152018-01-18 11:48:09 -0800120 }
121
122 @Override
Kevin Chyne9275662018-07-23 16:42:06 -0700123 public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
124 if (DEBUG) Log.d(TAG, "showBiometricDialog");
Kevin Chyn42653e82018-01-19 14:15:46 -0800125 // Remove these messages as they are part of the previous client
Kevin Chyne9275662018-07-23 16:42:06 -0700126 mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
127 mHandler.removeMessages(MSG_BIOMETRIC_HELP);
128 mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
Kevin Chyn42653e82018-01-19 14:15:46 -0800129 SomeArgs args = SomeArgs.obtain();
130 args.arg1 = bundle;
131 args.arg2 = receiver;
132 mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
Kevin Chynaae4a152018-01-18 11:48:09 -0800133 }
134
135 @Override
Kevin Chyne9275662018-07-23 16:42:06 -0700136 public void onBiometricAuthenticated() {
137 if (DEBUG) Log.d(TAG, "onBiometricAuthenticated");
138 mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
Kevin Chynaae4a152018-01-18 11:48:09 -0800139 }
140
141 @Override
Kevin Chyne9275662018-07-23 16:42:06 -0700142 public void onBiometricHelp(String message) {
143 if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
144 mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
Kevin Chynaae4a152018-01-18 11:48:09 -0800145 }
146
147 @Override
Kevin Chyne9275662018-07-23 16:42:06 -0700148 public void onBiometricError(String error) {
149 if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
150 mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
Kevin Chynaae4a152018-01-18 11:48:09 -0800151 }
152
153 @Override
Kevin Chyne9275662018-07-23 16:42:06 -0700154 public void hideBiometricDialog() {
155 if (DEBUG) Log.d(TAG, "hideBiometricDialog");
Kevin Chyn42653e82018-01-19 14:15:46 -0800156 mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
157 }
158
159 private void handleShowDialog(SomeArgs args) {
Kevin Chyn87df0682018-04-10 19:29:23 -0700160 if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
161 + mDialogView.isAnimatingAway());
162 if (mDialogView.isAnimatingAway()) {
163 mDialogView.forceRemove();
164 } else if (mDialogShowing) {
Kevin Chyn42653e82018-01-19 14:15:46 -0800165 Log.w(TAG, "Dialog already showing");
166 return;
167 }
Vishwath Mohanecf00ce2018-04-05 10:28:24 -0700168 mReceiver = (IBiometricPromptReceiver) args.arg2;
Kevin Chyn42653e82018-01-19 14:15:46 -0800169 mDialogView.setBundle((Bundle)args.arg1);
170 mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
171 mDialogShowing = true;
172 }
173
Kevin Chyne9275662018-07-23 16:42:06 -0700174 private void handleBiometricAuthenticated() {
175 if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
176
177 // TODO: announce correct string depending on modality
Kevin Chyn932e1df2018-03-26 11:53:47 -0700178 mDialogView.announceForAccessibility(
179 mContext.getResources().getText(
180 com.android.internal.R.string.fingerprint_authenticated));
Kevin Chyn42653e82018-01-19 14:15:46 -0800181 handleHideDialog(false /* userCanceled */);
182 }
183
Kevin Chyne9275662018-07-23 16:42:06 -0700184 private void handleBiometricHelp(String message) {
185 if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
Kevin Chyn42653e82018-01-19 14:15:46 -0800186 mDialogView.showHelpMessage(message);
187 }
188
Kevin Chyne9275662018-07-23 16:42:06 -0700189 private void handleBiometricError(String error) {
190 if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
Kevin Chyn42653e82018-01-19 14:15:46 -0800191 if (!mDialogShowing) {
192 if (DEBUG) Log.d(TAG, "Dialog already dismissed");
193 return;
194 }
195 mDialogView.showErrorMessage(error);
196 }
197
198 private void handleHideDialog(boolean userCanceled) {
Kevin Chyn87df0682018-04-10 19:29:23 -0700199 if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
Kevin Chyn42653e82018-01-19 14:15:46 -0800200 if (!mDialogShowing) {
201 // This can happen if there's a race and we get called from both
202 // onAuthenticated and onError, etc.
203 Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
204 return;
205 }
206 if (userCanceled) {
207 try {
Vishwath Mohanecf00ce2018-04-05 10:28:24 -0700208 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
Kevin Chyn42653e82018-01-19 14:15:46 -0800209 } catch (RemoteException e) {
210 Log.e(TAG, "RemoteException when hiding dialog", e);
211 }
212 }
213 mReceiver = null;
Kevin Chyn42653e82018-01-19 14:15:46 -0800214 mDialogShowing = false;
Kevin Chyn27e1f262018-03-08 16:38:32 -0800215 mDialogView.startDismiss();
Kevin Chyn42653e82018-01-19 14:15:46 -0800216 }
217
218 private void handleButtonNegative() {
219 if (mReceiver == null) {
220 Log.e(TAG, "Receiver is null");
221 return;
222 }
223 try {
Vishwath Mohanecf00ce2018-04-05 10:28:24 -0700224 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
Kevin Chyn42653e82018-01-19 14:15:46 -0800225 } catch (RemoteException e) {
226 Log.e(TAG, "Remote exception when handling negative button", e);
227 }
228 handleHideDialog(false /* userCanceled */);
229 }
230
231 private void handleButtonPositive() {
232 if (mReceiver == null) {
233 Log.e(TAG, "Receiver is null");
234 return;
235 }
236 try {
Vishwath Mohanecf00ce2018-04-05 10:28:24 -0700237 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
Kevin Chyn42653e82018-01-19 14:15:46 -0800238 } catch (RemoteException e) {
239 Log.e(TAG, "Remote exception when handling positive button", e);
240 }
241 handleHideDialog(false /* userCanceled */);
242 }
243
Kevin Chyn42653e82018-01-19 14:15:46 -0800244 private void handleUserCanceled() {
245 handleHideDialog(true /* userCanceled */);
Kevin Chynaae4a152018-01-18 11:48:09 -0800246 }
247}