• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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