1 /*
2  * Copyright (C) 2024 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 android.hardware.face;
18 
19 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_VENDOR;
20 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_VENDOR_BASE;
21 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR;
22 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR_BASE;
23 import static android.hardware.face.FaceManager.getAuthHelpMessage;
24 import static android.hardware.face.FaceManager.getEnrollHelpMessage;
25 import static android.hardware.face.FaceManager.getErrorString;
26 
27 import android.content.Context;
28 import android.hardware.biometrics.CryptoObject;
29 import android.hardware.face.FaceManager.AuthenticationCallback;
30 import android.hardware.face.FaceManager.EnrollmentCallback;
31 import android.hardware.face.FaceManager.FaceDetectionCallback;
32 import android.hardware.face.FaceManager.GenerateChallengeCallback;
33 import android.hardware.face.FaceManager.GetFeatureCallback;
34 import android.hardware.face.FaceManager.RemovalCallback;
35 import android.hardware.face.FaceManager.SetFeatureCallback;
36 import android.util.Slog;
37 
38 import androidx.annotation.NonNull;
39 import androidx.annotation.Nullable;
40 
41 /**
42  * Encapsulates callbacks and client specific information for each face related request.
43  * @hide
44  */
45 public class FaceCallback {
46     private static final String TAG = " FaceCallback";
47 
48     @Nullable
49     private AuthenticationCallback mAuthenticationCallback;
50     @Nullable
51     private EnrollmentCallback mEnrollmentCallback;
52     @Nullable
53     private RemovalCallback mRemovalCallback;
54     @Nullable
55     private GenerateChallengeCallback mGenerateChallengeCallback;
56     @Nullable
57     private FaceDetectionCallback mFaceDetectionCallback;
58     @Nullable
59     private SetFeatureCallback mSetFeatureCallback;
60     @Nullable
61     private GetFeatureCallback mGetFeatureCallback;
62     @Nullable
63     private Face mRemovalFace;
64     @Nullable
65     private CryptoObject mCryptoObject;
66 
67     /**
68      * Construction for face authentication client callback.
69      */
FaceCallback(AuthenticationCallback authenticationCallback, CryptoObject cryptoObject)70     FaceCallback(AuthenticationCallback authenticationCallback, CryptoObject cryptoObject) {
71         mAuthenticationCallback = authenticationCallback;
72         mCryptoObject = cryptoObject;
73     }
74 
75     /**
76      * Construction for face detect client callback.
77      */
FaceCallback(FaceDetectionCallback faceDetectionCallback)78     FaceCallback(FaceDetectionCallback faceDetectionCallback) {
79         mFaceDetectionCallback = faceDetectionCallback;
80     }
81 
82     /**
83      * Construction for face enroll client callback.
84      */
FaceCallback(EnrollmentCallback enrollmentCallback)85     FaceCallback(EnrollmentCallback enrollmentCallback) {
86         mEnrollmentCallback = enrollmentCallback;
87     }
88 
89     /**
90      * Construction for face generate challenge client callback.
91      */
FaceCallback(GenerateChallengeCallback generateChallengeCallback)92     FaceCallback(GenerateChallengeCallback generateChallengeCallback) {
93         mGenerateChallengeCallback = generateChallengeCallback;
94     }
95 
96     /**
97      * Construction for face set feature client callback.
98      */
FaceCallback(SetFeatureCallback setFeatureCallback)99     FaceCallback(SetFeatureCallback setFeatureCallback) {
100         mSetFeatureCallback = setFeatureCallback;
101     }
102 
103     /**
104      * Construction for face get feature client callback.
105      */
FaceCallback(GetFeatureCallback getFeatureCallback)106     FaceCallback(GetFeatureCallback getFeatureCallback) {
107         mGetFeatureCallback = getFeatureCallback;
108     }
109 
110     /**
111      * Construction for single face removal client callback.
112      */
FaceCallback(RemovalCallback removalCallback, Face removalFace)113     FaceCallback(RemovalCallback removalCallback, Face removalFace) {
114         mRemovalCallback = removalCallback;
115         mRemovalFace = removalFace;
116     }
117 
118     /**
119      * Construction for all face removal client callback.
120      */
FaceCallback(RemovalCallback removalCallback)121     FaceCallback(RemovalCallback removalCallback) {
122         mRemovalCallback = removalCallback;
123     }
124 
125     /**
126      * Propagate set feature completed via the callback.
127      * @param success if the operation was completed successfully
128      * @param feature the feature that was set
129      */
sendSetFeatureCompleted(boolean success, int feature)130     public void sendSetFeatureCompleted(boolean success, int feature) {
131         if (mSetFeatureCallback == null) {
132             return;
133         }
134         mSetFeatureCallback.onCompleted(success, feature);
135     }
136 
137     /**
138      * Propagate get feature completed via the callback.
139      * @param success if the operation was completed successfully
140      * @param features list of features available
141      * @param featureState status of the features corresponding to the previous parameter
142      */
sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState)143     public void sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState) {
144         if (mGetFeatureCallback == null) {
145             return;
146         }
147         mGetFeatureCallback.onCompleted(success, features, featureState);
148     }
149 
150     /**
151      * Propagate challenge generated completed via the callback.
152      * @param sensorId id of the corresponding sensor
153      * @param userId id of the corresponding sensor
154      * @param challenge value of the challenge generated
155      */
sendChallengeGenerated(int sensorId, int userId, long challenge)156     public void sendChallengeGenerated(int sensorId, int userId, long challenge) {
157         if (mGenerateChallengeCallback == null) {
158             return;
159         }
160         mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge);
161     }
162 
163     /**
164      * Propagate face detected completed via the callback.
165      * @param sensorId id of the corresponding sensor
166      * @param userId id of the corresponding user
167      * @param isStrongBiometric if the sensor is strong or not
168      */
sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric)169     public void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
170         if (mFaceDetectionCallback == null) {
171             Slog.e(TAG, "sendFaceDetected, callback null");
172             return;
173         }
174         mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric);
175     }
176 
177     /**
178      * Propagate remove face completed via the callback.
179      * @param face removed identifier
180      * @param remaining number of face enrollments remaining
181      */
sendRemovedResult(Face face, int remaining)182     public void sendRemovedResult(Face face, int remaining) {
183         if (mRemovalCallback == null) {
184             return;
185         }
186         mRemovalCallback.onRemovalSucceeded(face, remaining);
187     }
188 
189     /**
190      * Propagate errors via the callback.
191      * @param context corresponding context
192      * @param errMsgId represents the framework error id
193      * @param vendorCode represents the vendor error code
194      */
sendErrorResult(Context context, int errMsgId, int vendorCode)195     public void sendErrorResult(Context context, int errMsgId, int vendorCode) {
196         // emulate HAL 2.1 behavior and send real errMsgId
197         final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
198                 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
199         if (mEnrollmentCallback != null) {
200             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
201                     getErrorString(context, errMsgId, vendorCode));
202         } else if (mAuthenticationCallback != null) {
203             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
204                     getErrorString(context, errMsgId, vendorCode));
205         } else if (mRemovalCallback != null) {
206             mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
207                     getErrorString(context, errMsgId, vendorCode));
208         } else if (mFaceDetectionCallback != null) {
209             mFaceDetectionCallback.onDetectionError(errMsgId);
210             mFaceDetectionCallback = null;
211         }
212     }
213 
214     /**
215      * Propagate enroll progress via the callback.
216      * @param remaining number of enrollment steps remaining
217      */
sendEnrollResult(int remaining)218     public void sendEnrollResult(int remaining) {
219         if (mEnrollmentCallback != null) {
220             mEnrollmentCallback.onEnrollmentProgress(remaining);
221         }
222     }
223 
224     /**
225      * Propagate authentication succeeded via the callback.
226      * @param face matched identifier
227      * @param userId id of the corresponding user
228      * @param isStrongBiometric if the sensor is strong or not
229      */
sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric)230     public void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) {
231         if (mAuthenticationCallback != null) {
232             final FaceManager.AuthenticationResult result = new FaceManager.AuthenticationResult(
233                     mCryptoObject, face, userId, isStrongBiometric);
234             mAuthenticationCallback.onAuthenticationSucceeded(result);
235         }
236     }
237 
238     /**
239      * Propagate authentication failed via the callback.
240      */
sendAuthenticatedFailed()241     public void sendAuthenticatedFailed() {
242         if (mAuthenticationCallback != null) {
243             mAuthenticationCallback.onAuthenticationFailed();
244         }
245     }
246 
247     /**
248      * Propagate acquired result via the callback.
249      * @param context corresponding context
250      * @param acquireInfo represents the framework acquired id
251      * @param vendorCode represents the vendor acquired code
252      */
sendAcquiredResult(Context context, int acquireInfo, int vendorCode)253     public void sendAcquiredResult(Context context, int acquireInfo, int vendorCode) {
254         if (mAuthenticationCallback != null) {
255             final FaceAuthenticationFrame frame = new FaceAuthenticationFrame(
256                     new FaceDataFrame(acquireInfo, vendorCode));
257             sendAuthenticationFrame(context, frame);
258         } else if (mEnrollmentCallback != null) {
259             final FaceEnrollFrame frame = new FaceEnrollFrame(
260                     null /* cell */,
261                     FaceEnrollStages.UNKNOWN,
262                     new FaceDataFrame(acquireInfo, vendorCode));
263             sendEnrollmentFrame(context, frame);
264         }
265     }
266 
267     /**
268      * Propagate authentication frame via the callback.
269      * @param context corresponding context
270      * @param frame authentication frame to be sent
271      */
sendAuthenticationFrame(@onNull Context context, @Nullable FaceAuthenticationFrame frame)272     public void sendAuthenticationFrame(@NonNull Context context,
273             @Nullable FaceAuthenticationFrame frame) {
274         if (frame == null) {
275             Slog.w(TAG, "Received null authentication frame");
276         } else if (mAuthenticationCallback != null) {
277             // TODO(b/178414967): Send additional frame data to callback
278             final int acquireInfo = frame.getData().getAcquiredInfo();
279             final int vendorCode = frame.getData().getVendorCode();
280             final int helpCode = getHelpCode(acquireInfo, vendorCode);
281             final String helpMessage = getAuthHelpMessage(context, acquireInfo, vendorCode);
282             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
283 
284             // Ensure that only non-null help messages are sent.
285             if (helpMessage != null) {
286                 mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage);
287             }
288         }
289     }
290 
291     /**
292      * Propagate enrollment via the callback.
293      * @param context corresponding context
294      * @param frame enrollment frame to be sent
295      */
sendEnrollmentFrame(Context context, @Nullable FaceEnrollFrame frame)296     public void sendEnrollmentFrame(Context context, @Nullable FaceEnrollFrame frame) {
297         if (frame == null) {
298             Slog.w(TAG, "Received null enrollment frame");
299         } else if (mEnrollmentCallback != null) {
300             final FaceDataFrame data = frame.getData();
301             final int acquireInfo = data.getAcquiredInfo();
302             final int vendorCode = data.getVendorCode();
303             final int helpCode = getHelpCode(acquireInfo, vendorCode);
304             final String helpMessage = getEnrollHelpMessage(context, acquireInfo, vendorCode);
305             mEnrollmentCallback.onEnrollmentFrame(
306                     helpCode,
307                     helpMessage,
308                     frame.getCell(),
309                     frame.getStage(),
310                     data.getPan(),
311                     data.getTilt(),
312                     data.getDistance());
313         }
314     }
315 
getHelpCode(int acquireInfo, int vendorCode)316     private static int getHelpCode(int acquireInfo, int vendorCode) {
317         return acquireInfo == FACE_ACQUIRED_VENDOR
318                 ? vendorCode + FACE_ACQUIRED_VENDOR_BASE
319                 : acquireInfo;
320     }
321 }
322