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