Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 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 Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 14 | * limitations under the License |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 15 | */ |
| 16 | |
Kevin Chyn | 836f2cf | 2018-08-27 11:06:39 -0700 | [diff] [blame] | 17 | package com.android.server.biometrics; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 18 | |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 19 | import android.content.Context; |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 20 | import android.hardware.biometrics.BiometricAuthenticator; |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 21 | import android.hardware.biometrics.BiometricConstants; |
Beverly | d9ec6df | 2018-03-20 17:19:07 -0400 | [diff] [blame] | 22 | import android.media.AudioAttributes; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 23 | import android.os.IBinder; |
| 24 | import android.os.RemoteException; |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 25 | import android.os.VibrationEffect; |
| 26 | import android.os.Vibrator; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 27 | import android.util.Slog; |
| 28 | |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 29 | import com.android.internal.logging.MetricsLogger; |
| 30 | |
Kevin Chyn | 6cf54e8 | 2018-09-18 19:13:27 -0700 | [diff] [blame] | 31 | import java.util.ArrayList; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 32 | import java.util.NoSuchElementException; |
| 33 | |
| 34 | /** |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 35 | * Abstract base class for keeping track and dispatching events from the biometric's HAL to the |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 36 | * the current client. Subclasses are responsible for coordinating the interaction with |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 37 | * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.). |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 38 | */ |
| 39 | public abstract class ClientMonitor implements IBinder.DeathRecipient { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 40 | protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h. |
Kevin Chyn | 355c6bf | 2018-09-20 22:14:19 -0700 | [diff] [blame] | 41 | protected static final boolean DEBUG = BiometricServiceBase.DEBUG; |
Beverly | d9ec6df | 2018-03-20 17:19:07 -0400 | [diff] [blame] | 42 | private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES = |
| 43 | new AudioAttributes.Builder() |
| 44 | .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) |
| 45 | .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) |
| 46 | .build(); |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 47 | |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 48 | private final Context mContext; |
| 49 | private final long mHalDeviceId; |
| 50 | private final int mTargetUserId; |
| 51 | private final int mGroupId; |
| 52 | // True if client does not have MANAGE_FINGERPRINT permission |
| 53 | private final boolean mIsRestricted; |
| 54 | private final String mOwner; |
| 55 | private final VibrationEffect mSuccessVibrationEffect; |
| 56 | private final VibrationEffect mErrorVibrationEffect; |
Kevin Chyn | 355c6bf | 2018-09-20 22:14:19 -0700 | [diff] [blame] | 57 | private final BiometricServiceBase.DaemonWrapper mDaemon; |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 58 | |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 59 | private IBinder mToken; |
Kevin Chyn | 355c6bf | 2018-09-20 22:14:19 -0700 | [diff] [blame] | 60 | private BiometricServiceBase.ServiceListener mListener; |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 61 | |
| 62 | protected final MetricsLogger mMetricsLogger; |
| 63 | protected final Metrics mMetrics; |
| 64 | |
Kevin Chyn | 3736858 | 2017-05-19 17:15:38 -0700 | [diff] [blame] | 65 | protected boolean mAlreadyCancelled; |
Kevin Chyn | c79856b | 2018-10-05 18:57:35 -0700 | [diff] [blame] | 66 | protected boolean mAlreadyDone; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 67 | |
| 68 | /** |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 69 | * @param context context of BiometricService |
| 70 | * @param daemon interface to call back to a specific biometric's daemon |
| 71 | * @param halDeviceId the HAL device ID of the associated biometric hardware |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 72 | * @param token a unique token for the client |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 73 | * @param listener recipient of related events (e.g. authentication) |
Jim Miller | 8f2aca0 | 2016-04-20 13:34:11 -0700 | [diff] [blame] | 74 | * @param userId target user id for operation |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 75 | * @param groupId groupId for the fingerprint set |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 76 | * @param restricted whether or not client has the MANAGE_* permission |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 77 | * permission |
| 78 | * @param owner name of the client that owns this |
| 79 | */ |
Kevin Chyn | 355c6bf | 2018-09-20 22:14:19 -0700 | [diff] [blame] | 80 | public ClientMonitor(Context context, Metrics metrics, |
| 81 | BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, |
| 82 | BiometricServiceBase.ServiceListener listener, int userId, int groupId, |
| 83 | boolean restricted, String owner) { |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 84 | mContext = context; |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 85 | mMetrics = metrics; |
| 86 | mDaemon = daemon; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 87 | mHalDeviceId = halDeviceId; |
| 88 | mToken = token; |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 89 | mListener = listener; |
Jim Miller | 8f2aca0 | 2016-04-20 13:34:11 -0700 | [diff] [blame] | 90 | mTargetUserId = userId; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 91 | mGroupId = groupId; |
| 92 | mIsRestricted = restricted; |
| 93 | mOwner = owner; |
Ilya Matyukhin | 21a0d1e | 2018-04-26 15:57:29 -0700 | [diff] [blame] | 94 | mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 95 | mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 96 | mMetricsLogger = new MetricsLogger(); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 97 | try { |
Jim Miller | 7e1cb55 | 2017-02-27 17:37:32 -0800 | [diff] [blame] | 98 | if (token != null) { |
| 99 | token.linkToDeath(this, 0); |
| 100 | } |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 101 | } catch (RemoteException e) { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 102 | Slog.w(getLogTag(), "caught remote exception in linkToDeath: ", e); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 103 | } |
| 104 | } |
| 105 | |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 106 | protected String getLogTag() { |
| 107 | return mMetrics.logTag(); |
| 108 | } |
| 109 | |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 110 | /** |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 111 | * Contacts the biometric's HAL to start the client. |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 112 | * @return 0 on success, errno from driver on failure |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 113 | */ |
| 114 | public abstract int start(); |
| 115 | |
| 116 | /** |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 117 | * Contacts the biometric's HAL to stop the client. |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 118 | * @param initiatedByClient whether the operation is at the request of a client |
| 119 | */ |
| 120 | public abstract int stop(boolean initiatedByClient); |
| 121 | |
| 122 | /** |
| 123 | * Method to explicitly poke powermanager on events |
| 124 | */ |
| 125 | public abstract void notifyUserActivity(); |
| 126 | |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 127 | // Event callbacks from driver. Inappropriate calls is flagged/logged by the |
| 128 | // respective client (e.g. enrolling shouldn't get authenticate events). |
| 129 | // All of these return 'true' if the operation is completed and it's ok to move |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 130 | // to the next client (e.g. authentication accepts or rejects a biometric). |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 131 | public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, |
| 132 | int remaining); |
Kevin Chyn | b528d69 | 2018-07-20 11:53:14 -0700 | [diff] [blame] | 133 | public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, |
Kevin Chyn | 6cf54e8 | 2018-09-18 19:13:27 -0700 | [diff] [blame] | 134 | boolean authenticated, ArrayList<Byte> token); |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 135 | public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier, |
| 136 | int remaining); |
| 137 | public abstract boolean onEnumerationResult( |
| 138 | BiometricAuthenticator.Identifier identifier, int remaining); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 139 | |
Kevin Chyn | c79856b | 2018-10-05 18:57:35 -0700 | [diff] [blame] | 140 | |
| 141 | public boolean isAlreadyDone() { |
| 142 | return mAlreadyDone; |
| 143 | } |
| 144 | |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 145 | /** |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 146 | * Called when we get notification from the biometric's HAL that an image has been acquired. |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 147 | * Common to authenticate and enroll. |
| 148 | * @param acquiredInfo info about the current image acquisition |
| 149 | * @return true if client should be removed |
| 150 | */ |
Jim Miller | 40e4645 | 2016-12-16 18:38:53 -0800 | [diff] [blame] | 151 | public boolean onAcquired(int acquiredInfo, int vendorCode) { |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 152 | try { |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 153 | if (mListener != null) { |
| 154 | mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode); |
| 155 | } |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 156 | return false; // acquisition continues... |
| 157 | } catch (RemoteException e) { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 158 | Slog.w(getLogTag(), "Failed to invoke sendAcquired", e); |
| 159 | return true; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 160 | } finally { |
| 161 | // Good scans will keep the device awake |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 162 | if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 163 | notifyUserActivity(); |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | /** |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 169 | * Called when we get notification from the biometric's HAL that an error has occurred with the |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 170 | * current operation. Common to authenticate, enroll, enumerate and remove. |
| 171 | * @param error |
| 172 | * @return true if client should be removed |
| 173 | */ |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 174 | public boolean onError(long deviceId, int error, int vendorCode) { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 175 | try { |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 176 | if (mListener != null) { |
| 177 | mListener.onError(deviceId, error, vendorCode); |
| 178 | } |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 179 | } catch (RemoteException e) { |
| 180 | Slog.w(getLogTag(), "Failed to invoke sendError", e); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 181 | } |
| 182 | return true; // errors always remove current client |
| 183 | } |
| 184 | |
| 185 | public void destroy() { |
| 186 | if (mToken != null) { |
| 187 | try { |
| 188 | mToken.unlinkToDeath(this, 0); |
| 189 | } catch (NoSuchElementException e) { |
| 190 | // TODO: remove when duplicate call bug is found |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 191 | Slog.e(getLogTag(), "destroy(): " + this + ":", new Exception("here")); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 192 | } |
| 193 | mToken = null; |
| 194 | } |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 195 | mListener = null; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | @Override |
| 199 | public void binderDied() { |
| 200 | mToken = null; |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 201 | mListener = null; |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 202 | onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, |
| 203 | 0 /* vendorCode */); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | @Override |
| 207 | protected void finalize() throws Throwable { |
| 208 | try { |
| 209 | if (mToken != null) { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 210 | if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken); |
Kevin Chyn | a56dff7 | 2018-06-19 18:41:12 -0700 | [diff] [blame] | 211 | onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, |
| 212 | 0 /* vendorCode */); |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 213 | } |
| 214 | } finally { |
| 215 | super.finalize(); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | public final Context getContext() { |
| 220 | return mContext; |
| 221 | } |
| 222 | |
| 223 | public final long getHalDeviceId() { |
| 224 | return mHalDeviceId; |
| 225 | } |
| 226 | |
| 227 | public final String getOwnerString() { |
| 228 | return mOwner; |
| 229 | } |
| 230 | |
Kevin Chyn | 355c6bf | 2018-09-20 22:14:19 -0700 | [diff] [blame] | 231 | public final BiometricServiceBase.ServiceListener getListener() { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 232 | return mListener; |
| 233 | } |
| 234 | |
Kevin Chyn | 355c6bf | 2018-09-20 22:14:19 -0700 | [diff] [blame] | 235 | public final BiometricServiceBase.DaemonWrapper getDaemonWrapper() { |
Kevin Chyn | 037c4d5 | 2018-06-11 19:17:32 -0700 | [diff] [blame] | 236 | return mDaemon; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | public final boolean getIsRestricted() { |
| 240 | return mIsRestricted; |
| 241 | } |
| 242 | |
Jim Miller | 8f2aca0 | 2016-04-20 13:34:11 -0700 | [diff] [blame] | 243 | public final int getTargetUserId() { |
| 244 | return mTargetUserId; |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 245 | } |
| 246 | |
| 247 | public final int getGroupId() { |
| 248 | return mGroupId; |
| 249 | } |
| 250 | |
| 251 | public final IBinder getToken() { |
| 252 | return mToken; |
| 253 | } |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 254 | |
| 255 | public final void vibrateSuccess() { |
| 256 | Vibrator vibrator = mContext.getSystemService(Vibrator.class); |
| 257 | if (vibrator != null) { |
Beverly | d9ec6df | 2018-03-20 17:19:07 -0400 | [diff] [blame] | 258 | vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES); |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 259 | } |
| 260 | } |
| 261 | |
| 262 | public final void vibrateError() { |
| 263 | Vibrator vibrator = mContext.getSystemService(Vibrator.class); |
| 264 | if (vibrator != null) { |
Beverly | d9ec6df | 2018-03-20 17:19:07 -0400 | [diff] [blame] | 265 | vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES); |
Michael Wright | 6726fd5 | 2017-06-27 00:41:45 +0100 | [diff] [blame] | 266 | } |
| 267 | } |
Jim Miller | cb2ce6f | 2016-04-13 20:28:18 -0700 | [diff] [blame] | 268 | } |