blob: 9de018ee1e02fe3d184bf84e5a2e6799cd8930da [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
Kevin Chynf8688a02019-08-27 17:04:05 -070014 * limitations under the License.
Kevin Chynaae4a152018-01-18 11:48:09 -080015 */
16
Kevin Chyne9275662018-07-23 16:42:06 -070017package com.android.systemui.biometrics;
Kevin Chynaae4a152018-01-18 11:48:09 -080018
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070019import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
20import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
Curtis Belmonte13eb5812019-10-22 14:17:30 -070021import static android.hardware.biometrics.BiometricManager.Authenticators;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070022
Kevin Chyn050315f2019-08-08 14:22:54 -070023import android.app.ActivityManager;
24import android.app.ActivityTaskManager;
25import android.app.IActivityTaskManager;
26import android.app.TaskStackListener;
Kevin Chyne181b8d2019-11-05 15:02:52 -080027import android.content.BroadcastReceiver;
Kevin Chyn42653e82018-01-19 14:15:46 -080028import android.content.Context;
Kevin Chyne181b8d2019-11-05 15:02:52 -080029import android.content.Intent;
30import android.content.IntentFilter;
Gus Prevasa7df7b22018-10-30 10:29:34 -040031import android.content.res.Configuration;
Kevin Chyn8429da22019-09-24 12:42:35 -070032import android.hardware.biometrics.BiometricConstants;
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070033import android.hardware.biometrics.BiometricPrompt;
Kevin Chyn23289ef2018-11-28 16:32:36 -080034import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070035import android.hardware.face.FaceManager;
36import android.hardware.fingerprint.FingerprintManager;
Kevin Chynaae4a152018-01-18 11:48:09 -080037import android.os.Bundle;
Kevin Chyn050315f2019-08-08 14:22:54 -070038import android.os.Handler;
39import android.os.Looper;
Kevin Chyn42653e82018-01-19 14:15:46 -080040import android.os.RemoteException;
Kevin Chynaae4a152018-01-18 11:48:09 -080041import android.util.Log;
Kevin Chyn42653e82018-01-19 14:15:46 -080042import android.view.WindowManager;
Kevin Chynaae4a152018-01-18 11:48:09 -080043
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070044import com.android.internal.R;
Kevin Chyn050315f2019-08-08 14:22:54 -070045import com.android.internal.annotations.VisibleForTesting;
Kevin Chyn42653e82018-01-19 14:15:46 -080046import com.android.internal.os.SomeArgs;
Kevin Chynaae4a152018-01-18 11:48:09 -080047import com.android.systemui.SystemUI;
48import com.android.systemui.statusbar.CommandQueue;
49
Kevin Chyn050315f2019-08-08 14:22:54 -070050import java.util.List;
51
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040052import javax.inject.Inject;
53import javax.inject.Singleton;
54
Kevin Chyne9275662018-07-23 16:42:06 -070055/**
Kevin Chync53d9812019-07-30 18:10:30 -070056 * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
57 * appropriate biometric UI (e.g. BiometricDialogView).
Kevin Chyne9275662018-07-23 16:42:06 -070058 */
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040059@Singleton
Kevin Chynf8688a02019-08-27 17:04:05 -070060public class AuthController extends SystemUI implements CommandQueue.Callbacks,
61 AuthDialogCallback {
Kevin Chynfc468262019-08-20 17:17:11 -070062
Kevin Chynf8688a02019-08-27 17:04:05 -070063 private static final String TAG = "BiometricPrompt/AuthController";
Kevin Chyn42653e82018-01-19 14:15:46 -080064 private static final boolean DEBUG = true;
65
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040066 private final CommandQueue mCommandQueue;
Kevin Chyn050315f2019-08-08 14:22:54 -070067 private final Injector mInjector;
68
Kevin Chync53d9812019-07-30 18:10:30 -070069 // TODO: These should just be saved from onSaveState
Gus Prevasa7df7b22018-10-30 10:29:34 -040070 private SomeArgs mCurrentDialogArgs;
Kevin Chyn050315f2019-08-08 14:22:54 -070071 @VisibleForTesting
Kevin Chynf8688a02019-08-27 17:04:05 -070072 AuthDialog mCurrentDialog;
Kevin Chync53d9812019-07-30 18:10:30 -070073
Kevin Chyn050315f2019-08-08 14:22:54 -070074 private Handler mHandler = new Handler(Looper.getMainLooper());
Kevin Chyn42653e82018-01-19 14:15:46 -080075 private WindowManager mWindowManager;
Kevin Chyn050315f2019-08-08 14:22:54 -070076 @VisibleForTesting
77 IActivityTaskManager mActivityTaskManager;
78 @VisibleForTesting
79 BiometricTaskStackListener mTaskStackListener;
80 @VisibleForTesting
81 IBiometricServiceReceiverInternal mReceiver;
82
83 public class BiometricTaskStackListener extends TaskStackListener {
84 @Override
85 public void onTaskStackChanged() {
86 mHandler.post(mTaskStackChangedRunnable);
87 }
88 }
89
Kevin Chyne181b8d2019-11-05 15:02:52 -080090 @VisibleForTesting
91 final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
92 @Override
93 public void onReceive(Context context, Intent intent) {
94 if (mCurrentDialog != null
95 && Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
96 Log.w(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received");
97 mCurrentDialog.dismissWithoutCallback(true /* animate */);
98 mCurrentDialog = null;
99
100 try {
101 if (mReceiver != null) {
102 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
103 mReceiver = null;
104 }
105 } catch (RemoteException e) {
106 Log.e(TAG, "Remote exception", e);
107 }
108 }
109 }
110 };
111
Kevin Chyn050315f2019-08-08 14:22:54 -0700112 private final Runnable mTaskStackChangedRunnable = () -> {
113 if (mCurrentDialog != null) {
114 try {
115 final String clientPackage = mCurrentDialog.getOpPackageName();
116 Log.w(TAG, "Task stack changed, current client: " + clientPackage);
117 final List<ActivityManager.RunningTaskInfo> runningTasks =
118 mActivityTaskManager.getTasks(1);
119 if (!runningTasks.isEmpty()) {
120 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
121 if (!topPackage.contentEquals(clientPackage)) {
122 Log.w(TAG, "Evicting client due to: " + topPackage);
123 mCurrentDialog.dismissWithoutCallback(true /* animate */);
124 mCurrentDialog = null;
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700125 if (mReceiver != null) {
126 mReceiver.onDialogDismissed(
127 BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
128 mReceiver = null;
129 }
Kevin Chyn050315f2019-08-08 14:22:54 -0700130 }
131 }
132 } catch (RemoteException e) {
133 Log.e(TAG, "Remote exception", e);
134 }
135 }
136 };
Kevin Chyn42653e82018-01-19 14:15:46 -0800137
Kevin Chync53d9812019-07-30 18:10:30 -0700138 @Override
139 public void onTryAgainPressed() {
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700140 if (mReceiver == null) {
141 Log.e(TAG, "onTryAgainPressed: Receiver is null");
142 return;
143 }
Kevin Chync53d9812019-07-30 18:10:30 -0700144 try {
145 mReceiver.onTryAgainPressed();
146 } catch (RemoteException e) {
147 Log.e(TAG, "RemoteException when handling try again", e);
Kevin Chyn5906c172018-07-23 15:43:02 -0700148 }
149 }
150
Kevin Chync53d9812019-07-30 18:10:30 -0700151 @Override
Kevin Chynff168dc2019-09-16 16:04:38 -0700152 public void onDeviceCredentialPressed() {
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700153 if (mReceiver == null) {
154 Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
155 return;
156 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700157 try {
158 mReceiver.onDeviceCredentialPressed();
159 } catch (RemoteException e) {
160 Log.e(TAG, "RemoteException when handling credential button", e);
161 }
162 }
163
164 @Override
Kevin Chyn050315f2019-08-08 14:22:54 -0700165 public void onDismissed(@DismissedReason int reason) {
Kevin Chync53d9812019-07-30 18:10:30 -0700166 switch (reason) {
Kevin Chynf8688a02019-08-27 17:04:05 -0700167 case AuthDialogCallback.DISMISSED_USER_CANCELED:
Kevin Chync53d9812019-07-30 18:10:30 -0700168 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
169 break;
170
Kevin Chynf8688a02019-08-27 17:04:05 -0700171 case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE:
Kevin Chync53d9812019-07-30 18:10:30 -0700172 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
173 break;
174
Kevin Chynf8688a02019-08-27 17:04:05 -0700175 case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
Kevin Chynff168dc2019-09-16 16:04:38 -0700176 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
Kevin Chync53d9812019-07-30 18:10:30 -0700177 break;
178
Kevin Chynff168dc2019-09-16 16:04:38 -0700179 case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
180 sendResultAndCleanUp(
181 BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
Kevin Chync53d9812019-07-30 18:10:30 -0700182 break;
183
Kevin Chynf8688a02019-08-27 17:04:05 -0700184 case AuthDialogCallback.DISMISSED_ERROR:
Kevin Chync53d9812019-07-30 18:10:30 -0700185 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR);
Kevin Chync53d9812019-07-30 18:10:30 -0700186 break;
Kevin Chyn050315f2019-08-08 14:22:54 -0700187
Kevin Chynf8688a02019-08-27 17:04:05 -0700188 case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER:
Kevin Chyn050315f2019-08-08 14:22:54 -0700189 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
190 break;
191
Kevin Chynff168dc2019-09-16 16:04:38 -0700192 case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
193 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
194 break;
195
Kevin Chync53d9812019-07-30 18:10:30 -0700196 default:
197 Log.e(TAG, "Unhandled reason: " + reason);
198 break;
Kevin Chync94b7db2019-05-15 17:28:16 -0700199 }
Kevin Chync53d9812019-07-30 18:10:30 -0700200 }
201
Kevin Chyn050315f2019-08-08 14:22:54 -0700202 private void sendResultAndCleanUp(@DismissedReason int reason) {
Kevin Chync53d9812019-07-30 18:10:30 -0700203 if (mReceiver == null) {
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700204 Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
Kevin Chync53d9812019-07-30 18:10:30 -0700205 return;
206 }
207 try {
Kevin Chyn050315f2019-08-08 14:22:54 -0700208 mReceiver.onDialogDismissed(reason);
Kevin Chync53d9812019-07-30 18:10:30 -0700209 } catch (RemoteException e) {
210 Log.w(TAG, "Remote exception", e);
211 }
Kevin Chyn050315f2019-08-08 14:22:54 -0700212 onDialogDismissed(reason);
213 }
214
215 public static class Injector {
216 IActivityTaskManager getActivityTaskManager() {
217 return ActivityTaskManager.getService();
218 }
219 }
220
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400221 @Inject
222 public AuthController(Context context, CommandQueue commandQueue) {
223 this(context, commandQueue, new Injector());
Kevin Chyn050315f2019-08-08 14:22:54 -0700224 }
225
226 @VisibleForTesting
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400227 AuthController(Context context, CommandQueue commandQueue, Injector injector) {
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400228 super(context);
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400229 mCommandQueue = commandQueue;
Kevin Chyn050315f2019-08-08 14:22:54 -0700230 mInjector = injector;
Kevin Chyne181b8d2019-11-05 15:02:52 -0800231
232 IntentFilter filter = new IntentFilter();
233 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
234
235 context.registerReceiver(mBroadcastReceiver, filter);
Kevin Chync53d9812019-07-30 18:10:30 -0700236 }
Kevin Chync94b7db2019-05-15 17:28:16 -0700237
Kevin Chynaae4a152018-01-18 11:48:09 -0800238 @Override
239 public void start() {
Kevin Chyn4e6417d2020-02-20 12:10:21 -0800240 mCommandQueue.addCallback(this);
241 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
242 mActivityTaskManager = mInjector.getActivityTaskManager();
Kevin Chyn050315f2019-08-08 14:22:54 -0700243
Kevin Chyn4e6417d2020-02-20 12:10:21 -0800244 try {
245 mTaskStackListener = new BiometricTaskStackListener();
246 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
247 } catch (RemoteException e) {
248 Log.w(TAG, "Unable to register task stack listener", e);
Gus Prevasa7df7b22018-10-30 10:29:34 -0400249 }
250 }
251
Kevin Chynaae4a152018-01-18 11:48:09 -0800252 @Override
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700253 public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
254 int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
255 final int authenticators = Utils.getAuthenticators(bundle);
256
Kevin Chyn158fefb2019-01-03 18:59:05 -0800257 if (DEBUG) {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700258 Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
259 + ", biometricModality: " + biometricModality
Kevin Chyn158fefb2019-01-03 18:59:05 -0800260 + ", requireConfirmation: " + requireConfirmation);
261 }
Kevin Chyn42653e82018-01-19 14:15:46 -0800262 SomeArgs args = SomeArgs.obtain();
263 args.arg1 = bundle;
264 args.arg2 = receiver;
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700265 args.argi1 = biometricModality;
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700266 args.arg3 = requireConfirmation;
Kevin Chyn1b9f8df2018-11-12 19:04:55 -0800267 args.argi2 = userId;
Kevin Chyn050315f2019-08-08 14:22:54 -0700268 args.arg4 = opPackageName;
Kevin Chync53d9812019-07-30 18:10:30 -0700269
270 boolean skipAnimation = false;
271 if (mCurrentDialog != null) {
272 Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
273 skipAnimation = true;
274 }
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700275
276 showDialog(args, skipAnimation, null /* savedState */);
Kevin Chynaae4a152018-01-18 11:48:09 -0800277 }
278
279 @Override
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700280 public void onBiometricAuthenticated() {
281 mCurrentDialog.onAuthenticationSucceeded();
Kevin Chyn42653e82018-01-19 14:15:46 -0800282 }
283
Kevin Chync53d9812019-07-30 18:10:30 -0700284 @Override
285 public void onBiometricHelp(String message) {
286 if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
287
288 mCurrentDialog.onHelp(message);
Kevin Chyn42653e82018-01-19 14:15:46 -0800289 }
290
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700291 private String getErrorString(int modality, int error, int vendorCode) {
292 switch (modality) {
293 case TYPE_FACE:
294 return FaceManager.getErrorString(mContext, error, vendorCode);
Kevin Chyn8429da22019-09-24 12:42:35 -0700295
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700296 case TYPE_FINGERPRINT:
297 return FingerprintManager.getErrorString(mContext, error, vendorCode);
298
299 default:
300 return "";
301 }
302 }
303
304 @Override
305 public void onBiometricError(int modality, int error, int vendorCode) {
306 if (DEBUG) {
307 Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
308 }
309
310 final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
311 || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
312
313 // TODO(b/141025588): Create separate methods for handling hard and soft errors.
314 final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
315 || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
316
Kevin Chyn8429da22019-09-24 12:42:35 -0700317 if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700318 if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
Kevin Chyn8429da22019-09-24 12:42:35 -0700319 mCurrentDialog.animateToCredentialUI();
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700320 } else if (isSoftError) {
321 final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED)
322 ? mContext.getString(R.string.biometric_not_recognized)
323 : getErrorString(modality, error, vendorCode);
324 if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
325 mCurrentDialog.onAuthenticationFailed(errorMessage);
Kevin Chyn8429da22019-09-24 12:42:35 -0700326 } else {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700327 final String errorMessage = getErrorString(modality, error, vendorCode);
328 if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
329 mCurrentDialog.onError(errorMessage);
Kevin Chyn8429da22019-09-24 12:42:35 -0700330 }
Kevin Chyn42653e82018-01-19 14:15:46 -0800331 }
332
Kevin Chync53d9812019-07-30 18:10:30 -0700333 @Override
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700334 public void hideAuthenticationDialog() {
Kevin Chyna847a032020-01-17 14:17:03 -0800335 if (DEBUG) Log.d(TAG, "hideAuthenticationDialog: " + mCurrentDialog);
336
337 if (mCurrentDialog == null) {
338 // Could be possible if the caller canceled authentication after credential success
339 // but before the client was notified.
340 return;
341 }
Kevin Chync53d9812019-07-30 18:10:30 -0700342
Kevin Chyn050315f2019-08-08 14:22:54 -0700343 mCurrentDialog.dismissFromSystemServer();
Kevin Chyn22910722019-12-13 16:57:51 -0800344
345 // BiometricService will have already sent the callback to the client in this case.
346 // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done.
347 mCurrentDialog = null;
Kevin Chync53d9812019-07-30 18:10:30 -0700348 }
349
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700350 private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
Kevin Chync53d9812019-07-30 18:10:30 -0700351 mCurrentDialogArgs = args;
352 final int type = args.argi1;
Kevin Chyn050315f2019-08-08 14:22:54 -0700353 final Bundle biometricPromptBundle = (Bundle) args.arg1;
354 final boolean requireConfirmation = (boolean) args.arg3;
355 final int userId = args.argi2;
356 final String opPackageName = (String) args.arg4;
Kevin Chync53d9812019-07-30 18:10:30 -0700357
358 // Create a new dialog but do not replace the current one yet.
Kevin Chynf8688a02019-08-27 17:04:05 -0700359 final AuthDialog newDialog = buildDialog(
Kevin Chyn050315f2019-08-08 14:22:54 -0700360 biometricPromptBundle,
361 requireConfirmation,
362 userId,
363 type,
Kevin Chynfc468262019-08-20 17:17:11 -0700364 opPackageName,
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700365 skipAnimation);
Kevin Chync53d9812019-07-30 18:10:30 -0700366
367 if (newDialog == null) {
368 Log.e(TAG, "Unsupported type: " + type);
Kevin Chyn42653e82018-01-19 14:15:46 -0800369 return;
370 }
Kevin Chync53d9812019-07-30 18:10:30 -0700371
372 if (DEBUG) {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700373 Log.d(TAG, "showDialog: " + args
Kevin Chync53d9812019-07-30 18:10:30 -0700374 + " savedState: " + savedState
375 + " mCurrentDialog: " + mCurrentDialog
376 + " newDialog: " + newDialog
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700377 + " type: " + type);
Kevin Chync53d9812019-07-30 18:10:30 -0700378 }
379
Kevin Chyn9cf89912019-08-30 13:33:58 -0700380 if (mCurrentDialog != null) {
Kevin Chync53d9812019-07-30 18:10:30 -0700381 // If somehow we're asked to show a dialog, the old one doesn't need to be animated
382 // away. This can happen if the app cancels and re-starts auth during configuration
383 // change. This is ugly because we also have to do things on onConfigurationChanged
384 // here.
385 mCurrentDialog.dismissWithoutCallback(false /* animate */);
386 }
387
388 mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
389 mCurrentDialog = newDialog;
Kevin Chyn9cf89912019-08-30 13:33:58 -0700390 mCurrentDialog.show(mWindowManager, savedState);
Kevin Chync53d9812019-07-30 18:10:30 -0700391 }
392
Kevin Chyn050315f2019-08-08 14:22:54 -0700393 private void onDialogDismissed(@DismissedReason int reason) {
394 if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
Kevin Chync53d9812019-07-30 18:10:30 -0700395 if (mCurrentDialog == null) {
396 Log.w(TAG, "Dialog already dismissed");
Kevin Chyn42653e82018-01-19 14:15:46 -0800397 }
398 mReceiver = null;
Kevin Chync53d9812019-07-30 18:10:30 -0700399 mCurrentDialog = null;
Kevin Chyn23289ef2018-11-28 16:32:36 -0800400 }
401
Gus Prevasa7df7b22018-10-30 10:29:34 -0400402 @Override
403 protected void onConfigurationChanged(Configuration newConfig) {
Kevin Chyn02129b12018-11-01 16:47:12 -0700404 super.onConfigurationChanged(newConfig);
Kevin Chyne1912712019-01-04 14:22:34 -0800405
406 // Save the state of the current dialog (buttons showing, etc)
Kevin Chyne1912712019-01-04 14:22:34 -0800407 if (mCurrentDialog != null) {
Kevin Chync53d9812019-07-30 18:10:30 -0700408 final Bundle savedState = new Bundle();
Kevin Chyne1912712019-01-04 14:22:34 -0800409 mCurrentDialog.onSaveState(savedState);
Kevin Chync53d9812019-07-30 18:10:30 -0700410 mCurrentDialog.dismissWithoutCallback(false /* animate */);
411 mCurrentDialog = null;
Kevin Chyne1912712019-01-04 14:22:34 -0800412
Kevin Chyn27da7182019-09-11 12:17:55 -0700413 // Only show the dialog if necessary. If it was animating out, the dialog is supposed
414 // to send its pending callback immediately.
415 if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE)
416 != AuthContainerView.STATE_ANIMATING_OUT) {
Kevin Chyn8cbb4882019-09-19 16:49:02 -0700417 final boolean credentialShowing =
418 savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700419 if (credentialShowing) {
420 // TODO: Clean this up
421 Bundle bundle = (Bundle) mCurrentDialogArgs.arg1;
422 bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
Curtis Belmonte13eb5812019-10-22 14:17:30 -0700423 Authenticators.DEVICE_CREDENTIAL);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700424 }
Kevin Chyn8cbb4882019-09-19 16:49:02 -0700425
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700426 showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
Kevin Chyn27da7182019-09-11 12:17:55 -0700427 }
Gus Prevasa7df7b22018-10-30 10:29:34 -0400428 }
Kevin Chync53d9812019-07-30 18:10:30 -0700429 }
Kevin Chyne1912712019-01-04 14:22:34 -0800430
Kevin Chynf8688a02019-08-27 17:04:05 -0700431 protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700432 int userId, int type, String opPackageName, boolean skipIntro) {
Kevin Chynd837ced2019-09-11 16:09:43 -0700433 return new AuthContainerView.Builder(mContext)
434 .setCallback(this)
435 .setBiometricPromptBundle(biometricPromptBundle)
436 .setRequireConfirmation(requireConfirmation)
437 .setUserId(userId)
438 .setOpPackageName(opPackageName)
439 .setSkipIntro(skipIntro)
440 .build(type);
Gus Prevasa7df7b22018-10-30 10:29:34 -0400441 }
Kevin Chynaae4a152018-01-18 11:48:09 -0800442}