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