• 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.hardware.biometrics.BiometricsProtoEnums;
23 import android.os.IBinder;
24 import android.os.RemoteException;
25 import android.util.Slog;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.util.NoSuchElementException;
30 
31 /**
32  * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
33  * the current client.  Subclasses are responsible for coordinating the interaction with
34  * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
35  */
36 public abstract class BaseClientMonitor extends LoggableMonitor
37         implements IBinder.DeathRecipient {
38 
39     private static final String TAG = "Biometrics/ClientMonitor";
40     protected static final boolean DEBUG = true;
41 
42     // Counter used to distinguish between ClientMonitor instances to help debugging.
43     private static int sCount = 0;
44 
45     /**
46      * Interface that ClientMonitor holders should use to receive callbacks.
47      */
48     public interface Callback {
49 
50         /**
51          * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
52          * the queue and becomes the current operation).
53          *
54          * @param clientMonitor Reference of the ClientMonitor that is starting.
55          */
onClientStarted(@onNull BaseClientMonitor clientMonitor)56         default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
57         }
58 
59         /**
60          * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
61          * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
62          * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
63          * implementation.
64          *
65          * @param clientMonitor Reference of the ClientMonitor that finished.
66          * @param success True if the operation completed successfully.
67          */
onClientFinished(@onNull BaseClientMonitor clientMonitor, boolean success)68         default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
69         }
70     }
71 
72     /** Holder for wrapping multiple handlers into a single Callback. */
73     protected static class CompositeCallback implements Callback {
74         @NonNull
75         private final Callback[] mCallbacks;
76 
CompositeCallback(@onNull Callback... callbacks)77         public CompositeCallback(@NonNull Callback... callbacks) {
78             mCallbacks = callbacks;
79         }
80 
81         @Override
onClientStarted(@onNull BaseClientMonitor clientMonitor)82         public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
83             for (int i = 0; i < mCallbacks.length; i++) {
84                 mCallbacks[i].onClientStarted(clientMonitor);
85             }
86         }
87 
88         @Override
onClientFinished(@onNull BaseClientMonitor clientMonitor, boolean success)89         public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
90                 boolean success) {
91             for (int i = mCallbacks.length - 1; i >= 0; i--) {
92                 mCallbacks[i].onClientFinished(clientMonitor, success);
93             }
94         }
95     }
96 
97     private final int mSequentialId;
98     @NonNull private final Context mContext;
99     private final int mTargetUserId;
100     @NonNull private final String mOwner;
101     private final int mSensorId; // sensorId as configured by the framework
102 
103     @Nullable private IBinder mToken;
104     @Nullable private ClientMonitorCallbackConverter mListener;
105     // Currently only used for authentication client. The cookie generated by BiometricService
106     // is never 0.
107     private final int mCookie;
108     boolean mAlreadyDone;
109 
110     // Use an empty callback by default since delayed operations can receive events
111     // before they are started and cause NPE in subclasses that access this field directly.
112     @NonNull protected Callback mCallback = new Callback() {
113         @Override
114         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
115             Slog.e(TAG, "mCallback onClientStarted: called before set (should not happen)");
116         }
117 
118         @Override
119         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
120                 boolean success) {
121             Slog.e(TAG, "mCallback onClientFinished: called before set (should not happen)");
122         }
123     };
124 
125     /**
126      * @return A ClientMonitorEnum constant defined in biometrics.proto
127      */
getProtoEnum()128     public abstract int getProtoEnum();
129 
130     /**
131      * @return True if the ClientMonitor should cancel any current and pending interruptable clients
132      */
interruptsPrecedingClients()133     public boolean interruptsPrecedingClients() {
134         return false;
135     }
136 
137     /**
138      * @param context    system_server context
139      * @param token      a unique token for the client
140      * @param listener   recipient of related events (e.g. authentication)
141      * @param userId     target user id for operation
142      * @param owner      name of the client that owns this
143      * @param cookie     BiometricPrompt authentication cookie (to be moved into a subclass soon)
144      * @param sensorId   ID of the sensor that the operation should be requested of
145      * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
146      * @param statsAction   One of {@link BiometricsProtoEnums} ACTION_* constants
147      * @param statsClient   One of {@link BiometricsProtoEnums} CLIENT_* constants
148      */
BaseClientMonitor(@onNull Context context, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, int statsClient)149     public BaseClientMonitor(@NonNull Context context,
150             @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
151             @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
152             int statsClient) {
153         super(context, statsModality, statsAction, statsClient);
154         mSequentialId = sCount++;
155         mContext = context;
156         mToken = token;
157         mListener = listener;
158         mTargetUserId = userId;
159         mOwner = owner;
160         mCookie = cookie;
161         mSensorId = sensorId;
162 
163         try {
164             if (token != null) {
165                 token.linkToDeath(this, 0);
166             }
167         } catch (RemoteException e) {
168             Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
169         }
170     }
171 
getCookie()172     public int getCookie() {
173         return mCookie;
174     }
175 
176     /**
177      * Starts the ClientMonitor's lifecycle.
178      * @param callback invoked when the operation is complete (succeeds, fails, etc)
179      */
start(@onNull Callback callback)180     public void start(@NonNull Callback callback) {
181         mCallback = wrapCallbackForStart(callback);
182         mCallback.onClientStarted(this);
183     }
184 
185     /**
186      * Called during start to provide subclasses a hook for decorating the callback.
187      *
188      * Returns the original callback unless overridden.
189      */
190     @NonNull
wrapCallbackForStart(@onNull Callback callback)191     protected Callback wrapCallbackForStart(@NonNull Callback callback) {
192         return callback;
193     }
194 
isAlreadyDone()195     public boolean isAlreadyDone() {
196         return mAlreadyDone;
197     }
198 
destroy()199     public void destroy() {
200         if (mToken != null) {
201             try {
202                 mToken.unlinkToDeath(this, 0);
203             } catch (NoSuchElementException e) {
204                 // TODO: remove when duplicate call bug is found
205                 Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
206             }
207             mToken = null;
208         }
209         mListener = null;
210     }
211 
212     @Override
binderDied()213     public void binderDied() {
214         binderDiedInternal(true /* clearListener */);
215     }
216 
217     // TODO(b/157790417): Move this to the scheduler
binderDiedInternal(boolean clearListener)218     void binderDiedInternal(boolean clearListener) {
219         Slog.e(TAG, "Binder died, owner: " + getOwnerString()
220                 + ", operation: " + this.getClass().getName());
221 
222         if (isAlreadyDone()) {
223             Slog.w(TAG, "Binder died but client is finished, ignoring");
224             return;
225         }
226 
227         // If the current client dies we should cancel the current operation.
228         if (this instanceof Interruptable) {
229             Slog.e(TAG, "Binder died, cancelling client");
230             ((Interruptable) this).cancel();
231         }
232         mToken = null;
233         if (clearListener) {
234             mListener = null;
235         }
236     }
237 
getContext()238     public final Context getContext() {
239         return mContext;
240     }
241 
getOwnerString()242     public final String getOwnerString() {
243         return mOwner;
244     }
245 
getListener()246     public final ClientMonitorCallbackConverter getListener() {
247         return mListener;
248     }
249 
getTargetUserId()250     public int getTargetUserId() {
251         return mTargetUserId;
252     }
253 
getToken()254     public final IBinder getToken() {
255         return mToken;
256     }
257 
getSensorId()258     public final int getSensorId() {
259         return mSensorId;
260     }
261 
262     @VisibleForTesting
getCallback()263     public Callback getCallback() {
264         return mCallback;
265     }
266 
267     @Override
toString()268     public String toString() {
269         return "{[" + mSequentialId + "] "
270                 + this.getClass().getSimpleName()
271                 + ", proto=" + getProtoEnum()
272                 + ", owner=" + getOwnerString()
273                 + ", cookie=" + getCookie()
274                 + ", userId=" + getTargetUserId() + "}";
275     }
276 }
277