blob: afd1a94bf5069d092bf7510a5650ddedb73e0a18 [file] [log] [blame]
Jim Millercb2ce6f2016-04-13 20:28:18 -07001/**
2 * Copyright (C) 2016 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
17package com.android.server.fingerprint;
18
Jim Millercb2ce6f2016-04-13 20:28:18 -070019import android.content.Context;
Kevin Chynaae4a152018-01-18 11:48:09 -080020import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070021import android.hardware.biometrics.BiometricPrompt;
22import android.hardware.biometrics.IBiometricPromptReceiver;
Jim Millercb2ce6f2016-04-13 20:28:18 -070023import android.hardware.fingerprint.Fingerprint;
24import android.hardware.fingerprint.FingerprintManager;
Jim Millercb2ce6f2016-04-13 20:28:18 -070025import android.hardware.fingerprint.IFingerprintServiceReceiver;
Kevin Chynaae4a152018-01-18 11:48:09 -080026import android.os.Bundle;
Jim Millercb2ce6f2016-04-13 20:28:18 -070027import android.os.IBinder;
28import android.os.RemoteException;
Jim Millercb2ce6f2016-04-13 20:28:18 -070029import android.util.Slog;
30
Kevin Chynaae4a152018-01-18 11:48:09 -080031import com.android.internal.logging.MetricsLogger;
32import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
33import com.android.internal.statusbar.IStatusBarService;
34
Jim Millercb2ce6f2016-04-13 20:28:18 -070035/**
36 * A class to keep track of the authentication state for a given client.
37 */
38public abstract class AuthenticationClient extends ClientMonitor {
39 private long mOpId;
40
Kevin Chyndf9d33e2017-05-03 21:40:12 -070041 public abstract int handleFailedAttempt();
Jim Millercb2ce6f2016-04-13 20:28:18 -070042 public abstract void resetFailedAttempts();
Kevin Chyndf9d33e2017-05-03 21:40:12 -070043
44 public static final int LOCKOUT_NONE = 0;
45 public static final int LOCKOUT_TIMED = 1;
46 public static final int LOCKOUT_PERMANENT = 2;
47
Kevin Chynaae4a152018-01-18 11:48:09 -080048 // Callback mechanism received from the client
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070049 // (BiometricPrompt -> FingerprintManager -> FingerprintService -> AuthenticationClient)
50 private IBiometricPromptReceiver mDialogReceiverFromClient;
Kevin Chynaae4a152018-01-18 11:48:09 -080051 private Bundle mBundle;
52 private IStatusBarService mStatusBarService;
53 private boolean mInLockout;
54 private final FingerprintManager mFingerprintManager;
55 protected boolean mDialogDismissed;
56
Kevin Chyndba919a2018-03-16 14:35:10 -070057 // Receives events from SystemUI and handles them before forwarding them to FingerprintDialog
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070058 protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
Kevin Chynaae4a152018-01-18 11:48:09 -080059 @Override // binder call
60 public void onDialogDismissed(int reason) {
61 if (mBundle != null && mDialogReceiverFromClient != null) {
62 try {
63 mDialogReceiverFromClient.onDialogDismissed(reason);
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070064 if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
Kevin Chynaae4a152018-01-18 11:48:09 -080065 onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
66 0 /* vendorCode */);
67 }
68 mDialogDismissed = true;
69 } catch (RemoteException e) {
70 Slog.e(TAG, "Unable to notify dialog dismissed", e);
71 }
72 stop(true /* initiatedByClient */);
73 }
74 }
75 };
76
Kevin Chynd4f43c22018-03-12 17:33:13 -070077 /**
78 * This method is called when authentication starts.
79 */
80 public abstract void onStart();
81
82 /**
83 * This method is called when a fingerprint is authenticated or authentication is stopped
84 * (cancelled by the user, or an error such as lockout has occurred).
85 */
86 public abstract void onStop();
87
Jim Millercb2ce6f2016-04-13 20:28:18 -070088 public AuthenticationClient(Context context, long halDeviceId, IBinder token,
Jim Miller837fa7e2016-08-08 20:16:22 -070089 IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
Kevin Chynaae4a152018-01-18 11:48:09 -080090 boolean restricted, String owner, Bundle bundle,
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070091 IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
Jim Miller837fa7e2016-08-08 20:16:22 -070092 super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
Jim Millercb2ce6f2016-04-13 20:28:18 -070093 mOpId = opId;
Kevin Chynaae4a152018-01-18 11:48:09 -080094 mBundle = bundle;
95 mDialogReceiverFromClient = dialogReceiver;
96 mStatusBarService = statusBarService;
97 mFingerprintManager = (FingerprintManager) getContext()
98 .getSystemService(Context.FINGERPRINT_SERVICE);
99 }
100
101 @Override
102 public void binderDied() {
103 super.binderDied();
104 // When the binder dies, we should stop the client. This probably belongs in
105 // ClientMonitor's binderDied(), but testing all the cases would be tricky.
106 // AuthenticationClient is the most user-visible case.
107 stop(false /* initiatedByClient */);
108 }
109
110 @Override
111 public boolean onAcquired(int acquiredInfo, int vendorCode) {
112 // If the dialog is showing, the client doesn't need to receive onAcquired messages.
113 if (mBundle != null) {
114 try {
115 if (acquiredInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
116 mStatusBarService.onFingerprintHelp(
117 mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
118 }
119 return false; // acquisition continues
120 } catch (RemoteException e) {
121 Slog.e(TAG, "Remote exception when sending acquired message", e);
122 return true; // client failed
123 } finally {
124 // Good scans will keep the device awake
125 if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
126 notifyUserActivity();
127 }
128 }
129 } else {
130 return super.onAcquired(acquiredInfo, vendorCode);
131 }
132 }
133
134 @Override
135 public boolean onError(int error, int vendorCode) {
136 if (mDialogDismissed) {
137 // If user cancels authentication, the application has already received the
138 // FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED message from onDialogDismissed()
139 // and stopped the fingerprint hardware, so there is no need to send a
140 // FingerprintManager.FINGERPRINT_ERROR_CANCELED message.
141 return true;
142 }
143 if (mBundle != null) {
144 try {
145 mStatusBarService.onFingerprintError(
146 mFingerprintManager.getErrorString(error, vendorCode));
147 } catch (RemoteException e) {
148 Slog.e(TAG, "Remote exception when sending error", e);
149 }
150 }
151 return super.onError(error, vendorCode);
Jim Millercb2ce6f2016-04-13 20:28:18 -0700152 }
153
154 @Override
155 public boolean onAuthenticated(int fingerId, int groupId) {
156 boolean result = false;
157 boolean authenticated = fingerId != 0;
158
Kevin Chynaae4a152018-01-18 11:48:09 -0800159 // If the fingerprint dialog is showing, notify authentication succeeded
160 if (mBundle != null) {
161 try {
162 if (authenticated) {
163 mStatusBarService.onFingerprintAuthenticated();
164 } else {
165 mStatusBarService.onFingerprintHelp(getContext().getResources().getString(
166 com.android.internal.R.string.fingerprint_not_recognized));
167 }
168 } catch (RemoteException e) {
169 Slog.e(TAG, "Failed to notify Authenticated:", e);
170 }
171 }
172
Jim Millercb2ce6f2016-04-13 20:28:18 -0700173 IFingerprintServiceReceiver receiver = getReceiver();
174 if (receiver != null) {
175 try {
176 MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
177 authenticated);
178 if (!authenticated) {
179 receiver.onAuthenticationFailed(getHalDeviceId());
180 } else {
181 if (DEBUG) {
182 Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
183 + ", id=" + fingerId + ", gp=" + groupId + ")");
184 }
185 Fingerprint fp = !getIsRestricted()
186 ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
187 : null;
Jim Miller837fa7e2016-08-08 20:16:22 -0700188 receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
Jim Millercb2ce6f2016-04-13 20:28:18 -0700189 }
190 } catch (RemoteException e) {
191 Slog.w(TAG, "Failed to notify Authenticated:", e);
192 result = true; // client failed
193 }
194 } else {
195 result = true; // client not listening
196 }
Jim Millerd1974862016-05-03 18:35:18 -0700197 if (!authenticated) {
Jim Millercb2ce6f2016-04-13 20:28:18 -0700198 if (receiver != null) {
Michael Wright6726fd52017-06-27 00:41:45 +0100199 vibrateError();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700200 }
201 // allow system-defined limit of number of attempts before giving up
Kevin Chyndf9d33e2017-05-03 21:40:12 -0700202 int lockoutMode = handleFailedAttempt();
203 if (lockoutMode != LOCKOUT_NONE) {
Jim Millerd1974862016-05-03 18:35:18 -0700204 try {
Kevin Chynaae4a152018-01-18 11:48:09 -0800205 mInLockout = true;
Kevin Chyndf9d33e2017-05-03 21:40:12 -0700206 Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
207 lockoutMode + ")");
208 stop(false);
209 int errorCode = lockoutMode == LOCKOUT_TIMED ?
210 FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
211 FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
Kevin Chynaae4a152018-01-18 11:48:09 -0800212
213 // TODO: if the dialog is showing, this error should be delayed. On a similar
214 // note, AuthenticationClient should override onError and delay all other errors
215 // as well, if the dialog is showing
Kevin Chyndf9d33e2017-05-03 21:40:12 -0700216 receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
Kevin Chynaae4a152018-01-18 11:48:09 -0800217
218 // Send the lockout message to the system dialog
219 if (mBundle != null) {
220 mStatusBarService.onFingerprintError(
221 mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
222 }
Jim Millerd1974862016-05-03 18:35:18 -0700223 } catch (RemoteException e) {
224 Slog.w(TAG, "Failed to notify lockout:", e);
225 }
226 }
Kevin Chyndf9d33e2017-05-03 21:40:12 -0700227 result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
Jim Millercb2ce6f2016-04-13 20:28:18 -0700228 } else {
229 if (receiver != null) {
Michael Wright6726fd52017-06-27 00:41:45 +0100230 vibrateSuccess();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700231 }
232 result |= true; // we have a valid fingerprint, done
233 resetFailedAttempts();
Kevin Chynd4f43c22018-03-12 17:33:13 -0700234 onStop();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700235 }
236 return result;
237 }
238
239 /**
240 * Start authentication
241 */
242 @Override
243 public int start() {
Jim Miller40e46452016-12-16 18:38:53 -0800244 IBiometricsFingerprint daemon = getFingerprintDaemon();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700245 if (daemon == null) {
Kevin Chyn80e40cc2017-03-14 12:31:17 -0700246 Slog.w(TAG, "start authentication: no fingerprint HAL!");
Jim Millercb2ce6f2016-04-13 20:28:18 -0700247 return ERROR_ESRCH;
248 }
Kevin Chynd4f43c22018-03-12 17:33:13 -0700249 onStart();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700250 try {
251 final int result = daemon.authenticate(mOpId, getGroupId());
252 if (result != 0) {
253 Slog.w(TAG, "startAuthentication failed, result=" + result);
Jim Millerc57c8d92016-09-30 17:17:59 -0700254 MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
Jim Miller40e46452016-12-16 18:38:53 -0800255 onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
Jim Millercb2ce6f2016-04-13 20:28:18 -0700256 return result;
257 }
258 if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
Kevin Chynaae4a152018-01-18 11:48:09 -0800259
260 // If authenticating with system dialog, show the dialog
261 if (mBundle != null) {
262 try {
263 mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
264 } catch (RemoteException e) {
265 Slog.e(TAG, "Unable to show fingerprint dialog", e);
266 }
267 }
Jim Millercb2ce6f2016-04-13 20:28:18 -0700268 } catch (RemoteException e) {
269 Slog.e(TAG, "startAuthentication failed", e);
270 return ERROR_ESRCH;
271 }
272 return 0; // success
273 }
274
275 @Override
276 public int stop(boolean initiatedByClient) {
Kevin Chyn625a0142017-04-10 14:53:59 -0700277 if (mAlreadyCancelled) {
278 Slog.w(TAG, "stopAuthentication: already cancelled!");
279 return 0;
280 }
Kevin Chynaae4a152018-01-18 11:48:09 -0800281
Kevin Chynd4f43c22018-03-12 17:33:13 -0700282 onStop();
Jim Miller40e46452016-12-16 18:38:53 -0800283 IBiometricsFingerprint daemon = getFingerprintDaemon();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700284 if (daemon == null) {
Kevin Chyn80e40cc2017-03-14 12:31:17 -0700285 Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
Jim Millercb2ce6f2016-04-13 20:28:18 -0700286 return ERROR_ESRCH;
287 }
288 try {
Jim Miller40e46452016-12-16 18:38:53 -0800289 final int result = daemon.cancel();
Jim Millercb2ce6f2016-04-13 20:28:18 -0700290 if (result != 0) {
291 Slog.w(TAG, "stopAuthentication failed, result=" + result);
292 return result;
293 }
294 if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
295 } catch (RemoteException e) {
296 Slog.e(TAG, "stopAuthentication failed", e);
297 return ERROR_ESRCH;
Kevin Chynaae4a152018-01-18 11:48:09 -0800298 } finally {
299 // If the user already cancelled authentication (via some interaction with the
300 // dialog, we do not need to hide it since it's already hidden.
301 // If the device is in lockout, don't hide the dialog - it will automatically hide
Vishwath Mohanecf00ce2018-04-05 10:28:24 -0700302 // after BiometricPrompt.HIDE_DIALOG_DELAY
Kevin Chynaae4a152018-01-18 11:48:09 -0800303 if (mBundle != null && !mDialogDismissed && !mInLockout) {
304 try {
305 mStatusBarService.hideFingerprintDialog();
306 } catch (RemoteException e) {
307 Slog.e(TAG, "Unable to hide fingerprint dialog", e);
308 }
309 }
Jim Millercb2ce6f2016-04-13 20:28:18 -0700310 }
Kevin Chyn625a0142017-04-10 14:53:59 -0700311 mAlreadyCancelled = true;
Jim Millercb2ce6f2016-04-13 20:28:18 -0700312 return 0; // success
313 }
314
315 @Override
Jim Miller40e46452016-12-16 18:38:53 -0800316 public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
Jim Millercb2ce6f2016-04-13 20:28:18 -0700317 if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
318 return true; // Invalid for Authenticate
319 }
320
321 @Override
Jim Miller40e46452016-12-16 18:38:53 -0800322 public boolean onRemoved(int fingerId, int groupId, int remaining) {
Jim Millercb2ce6f2016-04-13 20:28:18 -0700323 if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
324 return true; // Invalid for Authenticate
325 }
326
327 @Override
Jim Miller40e46452016-12-16 18:38:53 -0800328 public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
Jim Millercb2ce6f2016-04-13 20:28:18 -0700329 if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
330 return true; // Invalid for Authenticate
331 }
332}