1 /* 2 * Copyright (C) 2020 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 static com.android.internal.annotations.VisibleForTesting.Visibility; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.hardware.biometrics.BiometricConstants; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.util.Slog; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.server.biometrics.log.BiometricContext; 31 import com.android.server.biometrics.log.BiometricLogger; 32 33 import java.util.NoSuchElementException; 34 35 /** 36 * Abstract base class for keeping track and dispatching events from the biometric's HAL to 37 * the current client. Subclasses are responsible for coordinating the interaction with 38 * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.). 39 */ 40 public abstract class BaseClientMonitor implements IBinder.DeathRecipient { 41 42 private static final String TAG = "BaseClientMonitor"; 43 protected static final boolean DEBUG = true; 44 45 // Counter used to distinguish between ClientMonitor instances to help debugging. 46 private static int sCount = 0; 47 48 private final int mSequentialId; 49 @NonNull private final Context mContext; 50 private final int mTargetUserId; 51 @NonNull private final String mOwner; 52 private final int mSensorId; // sensorId as configured by the framework 53 @NonNull private final BiometricLogger mLogger; 54 @NonNull private final BiometricContext mBiometricContext; 55 56 @Nullable private IBinder mToken; 57 private long mRequestId; 58 @Nullable private ClientMonitorCallbackConverter mListener; 59 // Currently only used for authentication client. The cookie generated by BiometricService 60 // is never 0. 61 private final int mCookie; 62 private boolean mAlreadyDone = false; 63 64 // Use an empty callback by default since delayed operations can receive events 65 // before they are started and cause NPE in subclasses that access this field directly. 66 @NonNull protected ClientMonitorCallback mCallback = new ClientMonitorCallback() { 67 @Override 68 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { 69 Slog.e(TAG, "mCallback onClientStarted: called before set (should not happen)"); 70 } 71 72 @Override 73 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, 74 boolean success) { 75 Slog.e(TAG, "mCallback onClientFinished: called before set (should not happen)"); 76 } 77 }; 78 79 /** 80 * @param context system_server context 81 * @param token a unique token for the client 82 * @param listener recipient of related events (e.g. authentication) 83 * @param userId target user id for operation 84 * @param owner name of the client that owns this 85 * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon) 86 * @param sensorId ID of the sensor that the operation should be requested of 87 * @param logger framework stats logger 88 * @param biometricContext system context metadata 89 */ BaseClientMonitor(@onNull Context context, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext)90 public BaseClientMonitor(@NonNull Context context, 91 @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, 92 @NonNull String owner, int cookie, int sensorId, 93 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { 94 mSequentialId = sCount++; 95 mContext = context; 96 mToken = token; 97 mRequestId = -1; 98 mListener = listener; 99 mTargetUserId = userId; 100 mOwner = owner; 101 mCookie = cookie; 102 mSensorId = sensorId; 103 mLogger = logger; 104 mBiometricContext = biometricContext; 105 106 try { 107 if (token != null) { 108 token.linkToDeath(this, 0); 109 } 110 } catch (RemoteException e) { 111 Slog.w(TAG, "caught remote exception in linkToDeath: ", e); 112 } 113 } 114 115 /** A ClientMonitorEnum constant defined in biometrics.proto */ getProtoEnum()116 public abstract int getProtoEnum(); 117 118 /** True if the ClientMonitor should cancel any current and pending interruptable clients. */ interruptsPrecedingClients()119 public boolean interruptsPrecedingClients() { 120 return false; 121 } 122 123 /** 124 * Sets the lifecycle callback before the operation is started via 125 * {@link #start(ClientMonitorCallback)} when the client must wait for a cookie before starting. 126 * 127 * @param callback lifecycle callback (typically same callback used for starting the operation) 128 */ waitForCookie(@onNull ClientMonitorCallback callback)129 public void waitForCookie(@NonNull ClientMonitorCallback callback) { 130 mCallback = callback; 131 } 132 133 /** 134 * Starts the ClientMonitor's lifecycle. 135 * @param callback invoked when the operation is complete (succeeds, fails, etc.) 136 */ start(@onNull ClientMonitorCallback callback)137 public void start(@NonNull ClientMonitorCallback callback) { 138 mCallback = wrapCallbackForStart(callback); 139 mCallback.onClientStarted(this); 140 } 141 142 /** 143 * Called during start to provide subclasses a hook for decorating the callback. 144 * 145 * Returns the original callback unless overridden. 146 */ 147 @NonNull wrapCallbackForStart(@onNull ClientMonitorCallback callback)148 protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { 149 return callback; 150 } 151 152 /** Signals this operation has completed its lifecycle and should no longer be used. */ 153 @VisibleForTesting(visibility = Visibility.PACKAGE) destroy()154 public void destroy() { 155 mAlreadyDone = true; 156 if (mToken != null) { 157 try { 158 mToken.unlinkToDeath(this, 0); 159 } catch (NoSuchElementException e) { 160 // TODO: remove when duplicate call bug is found 161 Slog.e(TAG, "destroy(): " + this + ":", new Exception("here")); 162 } 163 mToken = null; 164 } 165 } 166 167 /** 168 * Call while the operation is still active, but nearly done, to prevent any action 169 * upon client death (only needed for authentication clients). 170 */ markAlreadyDone()171 void markAlreadyDone() { 172 Slog.d(TAG, "marking operation as done: " + this); 173 mAlreadyDone = true; 174 } 175 176 /** If this operation has been marked as completely done (or cancelled). */ isAlreadyDone()177 public boolean isAlreadyDone() { 178 return mAlreadyDone; 179 } 180 181 @Override binderDied()182 public void binderDied() { 183 binderDiedInternal(true /* clearListener */); 184 } 185 186 // TODO(b/157790417): Move this to the scheduler binderDiedInternal(boolean clearListener)187 void binderDiedInternal(boolean clearListener) { 188 Slog.e(TAG, "Binder died, operation: " + this); 189 190 if (mAlreadyDone) { 191 Slog.w(TAG, "Binder died but client is finished, ignoring"); 192 return; 193 } 194 195 // If the current client dies we should cancel the current operation. 196 if (this.isInterruptable()) { 197 Slog.e(TAG, "Binder died, cancelling client"); 198 this.cancel(); 199 } 200 mToken = null; 201 if (clearListener) { 202 mListener = null; 203 } 204 } 205 206 /** 207 * Only valid for AuthenticationClient. 208 * @return true if the client is authenticating for a crypto operation. 209 */ isCryptoOperation()210 protected boolean isCryptoOperation() { 211 return false; 212 } 213 214 /** System context that may change during operations. */ 215 @NonNull getBiometricContext()216 protected BiometricContext getBiometricContext() { 217 return mBiometricContext; 218 } 219 220 /** Logger for this client */ 221 @NonNull getLogger()222 public BiometricLogger getLogger() { 223 return mLogger; 224 } 225 226 @NonNull getContext()227 public final Context getContext() { 228 return mContext; 229 } 230 231 @NonNull getOwnerString()232 public final String getOwnerString() { 233 return mOwner; 234 } 235 236 @Nullable getListener()237 public final ClientMonitorCallbackConverter getListener() { 238 return mListener; 239 } 240 getTargetUserId()241 public int getTargetUserId() { 242 return mTargetUserId; 243 } 244 245 @Nullable getToken()246 public final IBinder getToken() { 247 return mToken; 248 } 249 getSensorId()250 public int getSensorId() { 251 return mSensorId; 252 } 253 254 /** Cookie set when this monitor was created. */ getCookie()255 public int getCookie() { 256 return mCookie; 257 } 258 259 /** Unique request id. */ getRequestId()260 public long getRequestId() { 261 return mRequestId; 262 } 263 264 /** If a unique id has been set via {@link #setRequestId(long)} */ hasRequestId()265 public boolean hasRequestId() { 266 return mRequestId > 0; 267 } 268 269 /** 270 * A unique identifier used to tie this operation to a request (i.e an API invocation). 271 * 272 * Subclasses should not call this method if this operation does not have a direct 273 * correspondence to a request and {@link #hasRequestId()} will return false. 274 */ setRequestId(long id)275 protected final void setRequestId(long id) { 276 if (id <= 0) { 277 throw new IllegalArgumentException("request id must be positive"); 278 } 279 mRequestId = id; 280 } 281 282 @VisibleForTesting getCallback()283 public ClientMonitorCallback getCallback() { 284 return mCallback; 285 } 286 287 @Override toString()288 public String toString() { 289 return "{[" + mSequentialId + "] " 290 + this.getClass().getName() 291 + ", proto=" + getProtoEnum() 292 + ", owner=" + getOwnerString() 293 + ", cookie=" + getCookie() 294 + ", requestId=" + getRequestId() 295 + ", userId=" + getTargetUserId() + "}"; 296 } 297 298 /** 299 * Cancels this ClientMonitor 300 */ cancel()301 public void cancel() { 302 cancelWithoutStarting(mCallback); 303 } 304 305 /** 306 * Cancels this ClientMonitor without starting 307 * @param callback 308 */ cancelWithoutStarting(@onNull ClientMonitorCallback callback)309 public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) { 310 Slog.d(TAG, "cancelWithoutStarting: " + this); 311 312 final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED; 313 try { 314 ClientMonitorCallbackConverter listener = getListener(); 315 if (listener != null) { 316 listener.onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */); 317 } 318 } catch (RemoteException e) { 319 Slog.w(TAG, "Failed to invoke sendError", e); 320 } 321 callback.onClientFinished(this, true /* success */); 322 } 323 324 /** 325 * Checks if other client monitor can interrupt current client monitor 326 * @return if current client can be interrupted 327 */ isInterruptable()328 public boolean isInterruptable() { 329 return false; 330 } 331 } 332