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.content.Context;
21 import android.hardware.biometrics.BiometricConstants;
22 import android.os.IBinder;
23 import android.os.PowerManager;
24 import android.os.Process;
25 import android.os.RemoteException;
26 import android.os.SystemClock;
27 import android.os.VibrationAttributes;
28 import android.os.VibrationEffect;
29 import android.os.Vibrator;
30 import android.util.Slog;
31 
32 import com.android.server.biometrics.log.BiometricContext;
33 import com.android.server.biometrics.log.BiometricLogger;
34 
35 import java.util.function.Supplier;
36 
37 /**
38  * Abstract {@link HalClientMonitor} subclass that operations eligible/interested in acquisition
39  * messages should extend.
40  */
41 public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implements ErrorConsumer {
42 
43     private static final String TAG = "Biometrics/AcquisitionClient";
44 
45     private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
46             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
47 
48     private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
49             VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
50     private static final VibrationEffect ERROR_VIBRATION_EFFECT =
51             VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
52 
53     private final PowerManager mPowerManager;
54     // If haptics should occur when auth result (success/reject) is known
55     protected final boolean mShouldVibrate;
56     private boolean mShouldSendErrorToClient = true;
57     private boolean mAlreadyCancelled;
58 
AcquisitionClient(@onNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, boolean shouldVibrate, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext)59     public AcquisitionClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
60             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
61             @NonNull String owner, int cookie, int sensorId, boolean shouldVibrate,
62             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
63         super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId,
64                 logger, biometricContext);
65         mPowerManager = context.getSystemService(PowerManager.class);
66         mShouldVibrate = shouldVibrate;
67     }
68 
69     /**
70      * Stops the HAL operation specific to the ClientMonitor subclass.
71      */
stopHalOperation()72     protected abstract void stopHalOperation();
73 
74     @Override
unableToStart()75     public void unableToStart() {
76         try {
77             getListener().onError(getSensorId(), getCookie(),
78                     BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
79         } catch (RemoteException e) {
80             Slog.e(TAG, "Unable to send error", e);
81         }
82     }
83 
84     @Override
onError(int errorCode, int vendorCode)85     public void onError(int errorCode, int vendorCode) {
86         // Errors from the HAL always finish the client
87         onErrorInternal(errorCode, vendorCode, true /* finish */);
88     }
89 
90     /**
91      * Notifies the caller that the operation was canceled by the user. Note that the actual
92      * operation still needs to wait for the HAL to send ERROR_CANCELED.
93      */
onUserCanceled()94     public void onUserCanceled() {
95         Slog.d(TAG, "onUserCanceled");
96 
97         // Send USER_CANCELED, but do not finish. Wait for the HAL to respond with ERROR_CANCELED,
98         // which then finishes the AcquisitionClient's lifecycle.
99         onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, 0 /* vendorCode */,
100                 false /* finish */);
101         stopHalOperation();
102     }
103 
onErrorInternal(int errorCode, int vendorCode, boolean finish)104     protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) {
105         Slog.d(TAG, "onErrorInternal code: " + errorCode + ", finish: " + finish);
106 
107         // In some cases, the framework will send an error to the caller before a true terminal
108         // case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
109         // that do not handle lockout under the HAL. In these cases, ensure that the framework only
110         // sends errors once per ClientMonitor.
111         if (mShouldSendErrorToClient) {
112             getLogger().logOnError(getContext(), getOperationContext(),
113                     errorCode, vendorCode, getTargetUserId());
114             try {
115                 if (getListener() != null) {
116                     mShouldSendErrorToClient = false;
117                     getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
118                 }
119             } catch (RemoteException e) {
120                 Slog.w(TAG, "Failed to invoke sendError", e);
121             }
122         }
123 
124         if (finish) {
125             if (mCallback == null) {
126                 Slog.e(TAG, "Callback is null, perhaps the client hasn't been started yet?");
127             } else {
128                 mCallback.onClientFinished(this, false /* success */);
129             }
130         }
131     }
132 
133     @Override
cancel()134     public void cancel() {
135         if (mAlreadyCancelled) {
136             Slog.w(TAG, "Cancel was already requested");
137             return;
138         }
139 
140         stopHalOperation();
141         mAlreadyCancelled = true;
142     }
143 
144     @Override
cancelWithoutStarting(@onNull ClientMonitorCallback callback)145     public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
146         Slog.d(TAG, "cancelWithoutStarting: " + this);
147 
148         final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
149         try {
150             if (getListener() != null) {
151                 getListener().onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */);
152             }
153         } catch (RemoteException e) {
154             Slog.w(TAG, "Failed to invoke sendError", e);
155         }
156         callback.onClientFinished(this, true /* success */);
157     }
158 
159     /**
160      * Called when we get notification from the biometric's HAL that an image has been acquired.
161      * Common to authenticate and enroll.
162      * @param acquiredInfo info about the current image acquisition
163      */
onAcquired(int acquiredInfo, int vendorCode)164     public void onAcquired(int acquiredInfo, int vendorCode) {
165         // Default is to always send acquire messages to clients.
166         onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
167     }
168 
onAcquiredInternal(int acquiredInfo, int vendorCode, boolean shouldSend)169     protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
170             boolean shouldSend) {
171         getLogger().logOnAcquired(getContext(), getOperationContext(),
172                 acquiredInfo, vendorCode, getTargetUserId());
173         if (DEBUG) {
174             Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
175                     + ", shouldSend: " + shouldSend);
176         }
177 
178         // Good scans will keep the device awake
179         if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
180             notifyUserActivity();
181         }
182 
183         try {
184             if (getListener() != null && shouldSend) {
185                 getListener().onAcquired(getSensorId(), acquiredInfo, vendorCode);
186             }
187         } catch (RemoteException e) {
188             Slog.w(TAG, "Failed to invoke sendAcquired", e);
189             mCallback.onClientFinished(this, false /* success */);
190         }
191     }
192 
notifyUserActivity()193     final void notifyUserActivity() {
194         long now = SystemClock.uptimeMillis();
195         mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
196     }
197 
vibrateSuccess()198     protected final void vibrateSuccess() {
199         Vibrator vibrator = getContext().getSystemService(Vibrator.class);
200         if (vibrator != null && mShouldVibrate) {
201             vibrator.vibrate(Process.myUid(),
202                     getContext().getOpPackageName(),
203                     SUCCESS_VIBRATION_EFFECT,
204                     getClass().getSimpleName() + "::success",
205                     HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
206         }
207     }
208 
209     @Override
isInterruptable()210     public boolean isInterruptable() {
211         return true;
212     }
213 
isAlreadyCancelled()214     public boolean isAlreadyCancelled() {
215         return mAlreadyCancelled;
216     }
217 }
218