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