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