• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 #undef LOG_TAG
18 #define LOG_TAG "FaceVirtualHalEngine"
19 
20 #include "FakeFaceEngine.h"
21 
22 #include <android-base/logging.h>
23 
24 #include <face.sysprop.h>
25 
26 #include "util/CancellationSignal.h"
27 #include "util/Util.h"
28 
29 using namespace ::android::face::virt;
30 
31 namespace aidl::android::hardware::biometrics::face {
32 
GetSensorType()33 FaceSensorType FakeFaceEngine::GetSensorType() {
34     std::string type = FaceHalProperties::type().value_or("");
35     if (type == "IR") {
36         return FaceSensorType::IR;
37     } else {
38         FaceHalProperties::type("RGB");
39         return FaceSensorType::RGB;
40     }
41 }
42 
GetSensorStrength()43 common::SensorStrength FakeFaceEngine::GetSensorStrength() {
44     std::string strength = FaceHalProperties::strength().value_or("");
45     if (strength == "convenience") {
46         return common::SensorStrength::CONVENIENCE;
47     } else if (strength == "weak") {
48         return common::SensorStrength::WEAK;
49     } else {
50         FaceHalProperties::strength("strong");
51         return common::SensorStrength::STRONG;
52     }
53 }
54 
generateChallengeImpl(ISessionCallback * cb)55 void FakeFaceEngine::generateChallengeImpl(ISessionCallback* cb) {
56     BEGIN_OP(0);
57     std::uniform_int_distribution<int64_t> dist;
58     auto challenge = dist(mRandom);
59     FaceHalProperties::challenge(challenge);
60     cb->onChallengeGenerated(challenge);
61 }
62 
revokeChallengeImpl(ISessionCallback * cb,int64_t challenge)63 void FakeFaceEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
64     BEGIN_OP(0);
65     FaceHalProperties::challenge({});
66     cb->onChallengeRevoked(challenge);
67 }
getEnrollmentConfigImpl(ISessionCallback *,std::vector<EnrollmentStageConfig> *)68 void FakeFaceEngine::getEnrollmentConfigImpl(ISessionCallback* /*cb*/,
69                                              std::vector<EnrollmentStageConfig>* /*return_val*/) {}
enrollImpl(ISessionCallback * cb,const keymaster::HardwareAuthToken & hat,EnrollmentType,const std::vector<Feature> &,const std::future<void> & cancel)70 void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
71                                 EnrollmentType /*enrollmentType*/,
72                                 const std::vector<Feature>& /*features*/,
73                                 const std::future<void>& cancel) {
74     BEGIN_OP(getLatency(FaceHalProperties::operation_enroll_latency()));
75 
76     // Do proper HAT verification in the real implementation.
77     if (hat.mac.empty()) {
78         LOG(ERROR) << "Fail: hat";
79         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
80         return;
81     }
82 
83     // Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
84     // ------:-----------------------------------------:--------------
85     //          |           |                              |--->enrollment success (true/false)
86     //          |           |--> progress_steps
87     //          |
88     //          |-->enrollment id
89     //
90     //
91     //   progress_steps
92     //        <progress_duration>-[acquiredInfo,...]+
93     //        ----------------------------  ---------------------
94     //                 |                            |-> sequence of acquiredInfo code
95     //                 | --> time duration of the step in ms
96     //
97     //        E.g.   1:2000-[21,1108,5,6,1],1000-[1113,4,1]:true
98     //              A success enrollement of id 1 by 2 steps
99     //                    1st step lasts 2000ms with acquiredInfo codes (21,1108,5,6,1)
100     //                    2nd step lasts 1000ms with acquiredInfo codes (1113,4,1)
101     //
102     std::string defaultNextEnrollment =
103             "1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true";
104     auto nextEnroll = FaceHalProperties::next_enrollment().value_or(defaultNextEnrollment);
105     auto parts = Util::split(nextEnroll, ":");
106     if (parts.size() != 3) {
107         LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
108         cb->onError(Error::VENDOR, 0 /* vendorError */);
109         return;
110     }
111     auto enrollmentId = std::stoi(parts[0]);
112     auto progress = Util::parseEnrollmentCapture(parts[1]);
113     for (size_t i = 0; i < progress.size(); i += 2) {
114         auto left = (progress.size() - i) / 2 - 1;
115         auto duration = progress[i][0];
116         auto acquired = progress[i + 1];
117         auto N = acquired.size();
118 
119         for (int j = 0; j < N; j++) {
120             SLEEP_MS(duration / N);
121 
122             if (shouldCancel(cancel)) {
123                 LOG(ERROR) << "Fail: cancel";
124                 cb->onError(Error::CANCELED, 0 /* vendorCode */);
125                 return;
126             }
127             EnrollmentFrame frame = {};
128             auto ac = convertAcquiredInfo(acquired[j]);
129             frame.data.acquiredInfo = ac.first;
130             frame.data.vendorCode = ac.second;
131             frame.stage = (i == 0 && j == 0) ? EnrollmentStage::FIRST_FRAME_RECEIVED
132                           : (i == progress.size() - 2 && j == N - 1)
133                                   ? EnrollmentStage::ENROLLMENT_FINISHED
134                                   : EnrollmentStage::WAITING_FOR_CENTERING;
135             cb->onEnrollmentFrame(frame);
136         }
137 
138         if (left == 0 && !IS_TRUE(parts[2])) {  // end and failed
139             LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
140             FaceHalProperties::next_enrollment({});
141             cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
142         } else {  // progress and update props if last time
143             LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left;
144             if (left == 0) {
145                 auto enrollments = FaceHalProperties::enrollments();
146                 enrollments.emplace_back(enrollmentId);
147                 FaceHalProperties::enrollments(enrollments);
148                 FaceHalProperties::next_enrollment({});
149                 // change authenticatorId after new enrollment
150                 auto id = FaceHalProperties::authenticator_id().value_or(0);
151                 auto newId = id + 1;
152                 FaceHalProperties::authenticator_id(newId);
153                 LOG(INFO) << "Enrolled: " << enrollmentId;
154             }
155             cb->onEnrollmentProgress(enrollmentId, left);
156         }
157     }
158 }
159 
authenticateImpl(ISessionCallback * cb,int64_t,const std::future<void> & cancel)160 void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/,
161                                       const std::future<void>& cancel) {
162     BEGIN_OP(getLatency(FaceHalProperties::operation_authenticate_latency()));
163 
164     auto id = FaceHalProperties::enrollment_hit().value_or(0);
165     auto enrolls = FaceHalProperties::enrollments();
166     auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
167 
168     auto vec2str = [](std::vector<AcquiredInfo> va) {
169         std::stringstream ss;
170         bool isFirst = true;
171         for (auto ac : va) {
172             if (!isFirst) ss << ",";
173             ss << std::to_string((int8_t)ac);
174             isFirst = false;
175         }
176         return ss.str();
177     };
178 
179     // default behavior mimic face sensor in U
180     int64_t defaultAuthDuration = 500;
181     std::string defaultAcquiredInfo =
182             vec2str({AcquiredInfo::START, AcquiredInfo::FIRST_FRAME_RECEIVED});
183     if (!isEnrolled) {
184         std::vector<AcquiredInfo> v;
185         for (int i = 0; i < 56; i++) v.push_back(AcquiredInfo::NOT_DETECTED);
186         defaultAcquiredInfo += "," + vec2str(v);
187         defaultAuthDuration = 2100;
188     } else {
189         defaultAcquiredInfo += "," + vec2str({AcquiredInfo::TOO_BRIGHT, AcquiredInfo::TOO_BRIGHT,
190                                               AcquiredInfo::TOO_BRIGHT, AcquiredInfo::TOO_BRIGHT,
191                                               AcquiredInfo::GOOD, AcquiredInfo::GOOD});
192     }
193 
194     int64_t now = Util::getSystemNanoTime();
195     int64_t duration =
196             FaceHalProperties::operation_authenticate_duration().value_or(defaultAuthDuration);
197     auto acquired =
198             FaceHalProperties::operation_authenticate_acquired().value_or(defaultAcquiredInfo);
199     auto acquiredInfos = Util::parseIntSequence(acquired);
200     int N = acquiredInfos.size();
201 
202     if (N == 0) {
203         LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired;
204         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
205         return;
206     }
207 
208     if (mLockoutTracker.checkIfLockout(cb)) {
209         return;
210     }
211 
212     int i = 0;
213     do {
214         if (FaceHalProperties::lockout().value_or(false)) {
215             LOG(ERROR) << "Fail: lockout";
216             cb->onLockoutPermanent();
217             cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
218             return;
219         }
220 
221         if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
222             LOG(ERROR) << "Fail: operation_authenticate_fails";
223             mLockoutTracker.addFailedAttempt(cb);
224             cb->onAuthenticationFailed();
225             return;
226         }
227 
228         auto err = FaceHalProperties::operation_authenticate_error().value_or(0);
229         if (err != 0) {
230             LOG(ERROR) << "Fail: operation_authenticate_error";
231             auto ec = convertError(err);
232             cb->onError(ec.first, ec.second);
233             return; /* simply terminating current operation for any user inserted error,
234                             revisit if tests need*/
235         }
236 
237         if (shouldCancel(cancel)) {
238             LOG(ERROR) << "Fail: cancel";
239             cb->onError(Error::CANCELED, 0 /* vendorCode */);
240             return;
241         }
242 
243         if (i < N) {
244             auto ac = convertAcquiredInfo(acquiredInfos[i]);
245             AuthenticationFrame frame;
246             frame.data.acquiredInfo = ac.first;
247             frame.data.vendorCode = ac.second;
248             cb->onAuthenticationFrame(frame);
249             LOG(INFO) << "AcquiredInfo:" << i << ": (" << (int)ac.first << "," << (int)ac.second
250                       << ")";
251             i++;
252         }
253 
254         SLEEP_MS(duration / N);
255     } while (!Util::hasElapsed(now, duration));
256 
257     if (id > 0 && isEnrolled) {
258         mLockoutTracker.reset();
259         cb->onAuthenticationSucceeded(id, {} /* hat */);
260         return;
261     } else {
262         LOG(ERROR) << "Fail: face not enrolled";
263         mLockoutTracker.addFailedAttempt(cb);
264         cb->onAuthenticationFailed();
265         cb->onError(Error::TIMEOUT, 0 /* vendorError*/);
266         return;
267     }
268 }
269 
convertAcquiredInfo(int32_t code)270 std::pair<AcquiredInfo, int32_t> FakeFaceEngine::convertAcquiredInfo(int32_t code) {
271     std::pair<AcquiredInfo, int32_t> res;
272     if (code > FACE_ACQUIRED_VENDOR_BASE) {
273         res.first = AcquiredInfo::VENDOR;
274         res.second = code - FACE_ACQUIRED_VENDOR_BASE;
275     } else {
276         res.first = (AcquiredInfo)code;
277         res.second = 0;
278     }
279     return res;
280 }
281 
convertError(int32_t code)282 std::pair<Error, int32_t> FakeFaceEngine::convertError(int32_t code) {
283     std::pair<Error, int32_t> res;
284     if (code > FACE_ERROR_VENDOR_BASE) {
285         res.first = Error::VENDOR;
286         res.second = code - FACE_ERROR_VENDOR_BASE;
287     } else {
288         res.first = (Error)code;
289         res.second = 0;
290     }
291     return res;
292 }
293 
detectInteractionImpl(ISessionCallback * cb,const std::future<void> & cancel)294 void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel) {
295     BEGIN_OP(getLatency(FaceHalProperties::operation_detect_interaction_latency()));
296 
297     if (FaceHalProperties::operation_detect_interaction_fails().value_or(false)) {
298         LOG(ERROR) << "Fail: operation_detect_interaction_fails";
299         cb->onError(Error::VENDOR, 0 /* vendorError */);
300         return;
301     }
302 
303     if (shouldCancel(cancel)) {
304         LOG(ERROR) << "Fail: cancel";
305         cb->onError(Error::CANCELED, 0 /* vendorCode */);
306         return;
307     }
308 
309     auto id = FaceHalProperties::enrollment_hit().value_or(0);
310     auto enrolls = FaceHalProperties::enrollments();
311     auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
312     if (id <= 0 || !isEnrolled) {
313         LOG(ERROR) << "Fail: not enrolled";
314         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
315         return;
316     }
317 
318     cb->onInteractionDetected();
319 }
320 
enumerateEnrollmentsImpl(ISessionCallback * cb)321 void FakeFaceEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
322     BEGIN_OP(0);
323     std::vector<int32_t> enrollments;
324     for (const auto& enrollmentId : FaceHalProperties::enrollments()) {
325         if (enrollmentId) {
326             enrollments.push_back(*enrollmentId);
327         }
328     }
329     cb->onEnrollmentsEnumerated(enrollments);
330 }
331 
removeEnrollmentsImpl(ISessionCallback * cb,const std::vector<int32_t> & enrollmentIds)332 void FakeFaceEngine::removeEnrollmentsImpl(ISessionCallback* cb,
333                                            const std::vector<int32_t>& enrollmentIds) {
334     BEGIN_OP(0);
335 
336     std::vector<std::optional<int32_t>> newEnrollments;
337     for (const auto& enrollment : FaceHalProperties::enrollments()) {
338         auto id = enrollment.value_or(0);
339         if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) == enrollmentIds.end()) {
340             newEnrollments.emplace_back(id);
341         }
342     }
343     FaceHalProperties::enrollments(newEnrollments);
344     cb->onEnrollmentsRemoved(enrollmentIds);
345 }
346 
getFeaturesImpl(ISessionCallback * cb)347 void FakeFaceEngine::getFeaturesImpl(ISessionCallback* cb) {
348     BEGIN_OP(0);
349 
350     if (FaceHalProperties::enrollments().empty()) {
351         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
352         return;
353     }
354 
355     std::vector<Feature> featuresToReturn = {};
356     for (const auto& feature : FaceHalProperties::features()) {
357         if (feature) {
358             featuresToReturn.push_back((Feature)(*feature));
359         }
360     }
361     cb->onFeaturesRetrieved(featuresToReturn);
362 }
363 
setFeatureImpl(ISessionCallback * cb,const keymaster::HardwareAuthToken & hat,Feature feature,bool enabled)364 void FakeFaceEngine::setFeatureImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
365                                     Feature feature, bool enabled) {
366     BEGIN_OP(0);
367 
368     if (FaceHalProperties::enrollments().empty()) {
369         LOG(ERROR) << "Unable to set feature, enrollments are empty";
370         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
371         return;
372     }
373 
374     if (hat.mac.empty()) {
375         LOG(ERROR) << "Unable to set feature, invalid hat";
376         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
377         return;
378     }
379 
380     auto features = FaceHalProperties::features();
381 
382     auto itr = std::find_if(features.begin(), features.end(), [feature](const auto& theFeature) {
383         return *theFeature == (int)feature;
384     });
385 
386     if (!enabled && (itr != features.end())) {
387         features.erase(itr);
388     } else if (enabled && (itr == features.end())) {
389         features.push_back((int)feature);
390     }
391 
392     FaceHalProperties::features(features);
393     cb->onFeatureSet(feature);
394 }
395 
getAuthenticatorIdImpl(ISessionCallback * cb)396 void FakeFaceEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
397     BEGIN_OP(0);
398     // If this is a weak HAL return 0 per the spec.
399     if (GetSensorStrength() != common::SensorStrength::STRONG) {
400         cb->onAuthenticatorIdRetrieved(0);
401     } else {
402         cb->onAuthenticatorIdRetrieved(FaceHalProperties::authenticator_id().value_or(0));
403     }
404 }
405 
invalidateAuthenticatorIdImpl(ISessionCallback * cb)406 void FakeFaceEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
407     BEGIN_OP(0);
408     int64_t authenticatorId = FaceHalProperties::authenticator_id().value_or(0);
409     int64_t newId = authenticatorId + 1;
410     FaceHalProperties::authenticator_id(newId);
411     cb->onAuthenticatorIdInvalidated(newId);
412 }
413 
resetLockoutImpl(ISessionCallback * cb,const keymaster::HardwareAuthToken &)414 void FakeFaceEngine::resetLockoutImpl(ISessionCallback* cb,
415                                       const keymaster::HardwareAuthToken& /*hat*/) {
416     BEGIN_OP(0);
417     FaceHalProperties::lockout(false);
418     mLockoutTracker.reset();
419     cb->onLockoutCleared();
420 }
421 
getRandomInRange(int32_t bound1,int32_t bound2)422 int32_t FakeFaceEngine::getRandomInRange(int32_t bound1, int32_t bound2) {
423     std::uniform_int_distribution<int32_t> dist(std::min(bound1, bound2), std::max(bound1, bound2));
424     return dist(mRandom);
425 }
426 
getLatency(const std::vector<std::optional<std::int32_t>> & latencyIn)427 int32_t FakeFaceEngine::getLatency(const std::vector<std::optional<std::int32_t>>& latencyIn) {
428     int32_t res = DEFAULT_LATENCY;
429 
430     std::vector<int32_t> latency;
431     for (auto x : latencyIn)
432         if (x.has_value()) latency.push_back(*x);
433 
434     switch (latency.size()) {
435         case 0:
436             break;
437         case 1:
438             res = latency[0];
439             break;
440         case 2:
441             res = getRandomInRange(latency[0], latency[1]);
442             break;
443         default:
444             LOG(ERROR) << "ERROR: unexpected input of size " << latency.size();
445             break;
446     }
447 
448     return res;
449 }
450 
451 }  // namespace aidl::android::hardware::biometrics::face
452