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