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