blob: 69c9bf457b2939a3d3af8b56b72a434fe1581daf [file] [log] [blame]
Joshua McCloskeydb009a52022-05-10 05:18:20 +00001/*
2 * Copyright (C) 2022 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#include <android/binder_process.h>
18#include <face.sysprop.h>
19#include <gtest/gtest.h>
20
21#include <aidl/android/hardware/biometrics/face/BnSessionCallback.h>
22#include <android-base/logging.h>
23
24#include "FakeFaceEngine.h"
25
26using namespace ::android::face::virt;
27using namespace ::aidl::android::hardware::biometrics::face;
28using namespace ::aidl::android::hardware::keymaster;
29
30namespace aidl::android::hardware::biometrics::face {
31
32class TestSessionCallback : public BnSessionCallback {
33 public:
34 ndk::ScopedAStatus onChallengeGenerated(int64_t challenge) override {
35 mLastChallenge = challenge;
36 return ndk::ScopedAStatus::ok();
37 };
38 ::ndk::ScopedAStatus onChallengeRevoked(int64_t challenge) override {
39 mLastChallengeRevoked = challenge;
40 return ndk::ScopedAStatus::ok();
41 };
42 ::ndk::ScopedAStatus onError(Error error, int32_t) override {
43 mError = error;
44 return ndk::ScopedAStatus::ok();
45 };
46 ::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
47 if (remaining == 0) mLastEnrolled = enrollmentId;
Jeff Pu84c60182023-12-04 16:53:04 +000048 mRemaining = remaining;
Joshua McCloskeydb009a52022-05-10 05:18:20 +000049 return ndk::ScopedAStatus::ok();
50 };
51
52 ::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t enrollmentId,
53 const HardwareAuthToken&) override {
54 mLastAuthenticated = enrollmentId;
55 mAuthenticateFailed = false;
56 return ndk::ScopedAStatus::ok();
57 };
58 ::ndk::ScopedAStatus onAuthenticationFailed() override {
59 mLastAuthenticated = 0;
60 mAuthenticateFailed = true;
61 return ndk::ScopedAStatus::ok();
62 };
63 ::ndk::ScopedAStatus onInteractionDetected() override {
64 mInteractionDetectedCount++;
65 return ndk::ScopedAStatus::ok();
66 };
67
68 ::ndk::ScopedAStatus onEnrollmentFrame(const EnrollmentFrame& frame) override {
69 mEnrollmentFrames.push_back(frame.data.vendorCode);
70 return ndk::ScopedAStatus::ok();
71 }
72
73 ::ndk::ScopedAStatus onEnrollmentsEnumerated(
74 const std::vector<int32_t>& enrollmentIds) override {
75 mLastEnrollmentsEnumerated = enrollmentIds;
76 return ndk::ScopedAStatus::ok();
77 };
78 ::ndk::ScopedAStatus onEnrollmentsRemoved(const std::vector<int32_t>& enrollmentIds) override {
79 mLastEnrollmentRemoved = enrollmentIds;
80 return ndk::ScopedAStatus::ok();
81 };
82 ::ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t authenticatorId) override {
83 mLastAuthenticatorId = authenticatorId;
84 return ndk::ScopedAStatus::ok();
85 };
86 ::ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t authenticatorId) override {
87 mLastAuthenticatorId = authenticatorId;
88 mAuthenticatorIdInvalidated = true;
89 return ndk::ScopedAStatus::ok();
90 };
91 ::ndk::ScopedAStatus onAuthenticationFrame(const AuthenticationFrame& /*authFrame*/) override {
92 return ndk::ScopedAStatus::ok();
93 }
94 ::ndk::ScopedAStatus onLockoutPermanent() override {
95 mLockoutPermanent = true;
96 return ndk::ScopedAStatus::ok();
97 };
98 ::ndk::ScopedAStatus onLockoutTimed(int64_t /* timeout */) override {
99 return ndk::ScopedAStatus::ok();
100 }
101 ::ndk::ScopedAStatus onLockoutCleared() override {
102 mLockoutPermanent = false;
103 return ndk::ScopedAStatus::ok();
104 }
105 ::ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
106
107 ::ndk::ScopedAStatus onFeaturesRetrieved(const std::vector<Feature>& features) override {
108 mFeatures = features;
109 return ndk::ScopedAStatus::ok();
110 }
111
112 ::ndk::ScopedAStatus onFeatureSet(Feature feature) override {
113 mLastFeatureSet = feature;
114 return ndk::ScopedAStatus::ok();
115 }
116
117 Error mError = Error::UNKNOWN;
118 int64_t mLastChallenge = -1;
119 int64_t mLastChallengeRevoked = -1;
120 int32_t mLastEnrolled = -1;
121 int32_t mLastAuthenticated = -1;
122 int64_t mLastAuthenticatorId = -1;
123 std::vector<int32_t> mLastEnrollmentsEnumerated;
124 std::vector<int32_t> mLastEnrollmentRemoved;
125 std::vector<Feature> mFeatures;
126 Feature mLastFeatureSet;
127 std::vector<int32_t> mEnrollmentFrames;
128 bool mAuthenticateFailed = false;
129 bool mAuthenticatorIdInvalidated = false;
130 bool mLockoutPermanent = false;
131 int mInteractionDetectedCount = 0;
Jeff Pu84c60182023-12-04 16:53:04 +0000132 int mRemaining = -1;
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000133};
134
135class FakeFaceEngineTest : public ::testing::Test {
136 protected:
137 void SetUp() override {
138 LOG(ERROR) << "JRM SETUP";
139 mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
140 FaceHalProperties::enrollments({});
141 FaceHalProperties::challenge({});
142 FaceHalProperties::features({});
143 FaceHalProperties::authenticator_id({});
144 FaceHalProperties::strength("");
145 }
146
147 FakeFaceEngine mEngine;
148 std::shared_ptr<TestSessionCallback> mCallback;
149 std::promise<void> mCancel;
150};
151
152TEST_F(FakeFaceEngineTest, one_eq_one) {
153 ASSERT_EQ(1, 1);
154}
155
156TEST_F(FakeFaceEngineTest, GenerateChallenge) {
157 mEngine.generateChallengeImpl(mCallback.get());
158 ASSERT_EQ(FaceHalProperties::challenge().value(), mCallback->mLastChallenge);
159}
160
161TEST_F(FakeFaceEngineTest, RevokeChallenge) {
162 auto challenge = FaceHalProperties::challenge().value_or(10);
163 mEngine.revokeChallengeImpl(mCallback.get(), challenge);
164 ASSERT_FALSE(FaceHalProperties::challenge().has_value());
165 ASSERT_EQ(challenge, mCallback->mLastChallengeRevoked);
166}
167
168TEST_F(FakeFaceEngineTest, ResetLockout) {
169 FaceHalProperties::lockout(true);
170 mEngine.resetLockoutImpl(mCallback.get(), {});
171 ASSERT_FALSE(mCallback->mLockoutPermanent);
172 ASSERT_FALSE(FaceHalProperties::lockout().value_or(true));
173}
174
175TEST_F(FakeFaceEngineTest, AuthenticatorId) {
176 FaceHalProperties::authenticator_id(50);
177 mEngine.getAuthenticatorIdImpl(mCallback.get());
178 ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
179 ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
180}
181
182TEST_F(FakeFaceEngineTest, GetAuthenticatorIdWeakReturnsZero) {
183 FaceHalProperties::strength("weak");
184 FaceHalProperties::authenticator_id(500);
185 mEngine.getAuthenticatorIdImpl(mCallback.get());
186 ASSERT_EQ(0, mCallback->mLastAuthenticatorId);
187 ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
188}
189
190TEST_F(FakeFaceEngineTest, AuthenticatorIdInvalidate) {
191 FaceHalProperties::authenticator_id(500);
192 mEngine.invalidateAuthenticatorIdImpl(mCallback.get());
193 ASSERT_NE(500, FaceHalProperties::authenticator_id().value());
194 ASSERT_TRUE(mCallback->mAuthenticatorIdInvalidated);
195}
196
197TEST_F(FakeFaceEngineTest, Enroll) {
Jeff Pu84c60182023-12-04 16:53:04 +0000198 FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000199 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
200 mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
201 mCancel.get_future());
202 ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
203 ASSERT_EQ(1, FaceHalProperties::enrollments().size());
204 ASSERT_EQ(1, FaceHalProperties::enrollments()[0].value());
205 ASSERT_EQ(1, mCallback->mLastEnrolled);
Jeff Pu84c60182023-12-04 16:53:04 +0000206 ASSERT_EQ(0, mCallback->mRemaining);
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000207}
208
209TEST_F(FakeFaceEngineTest, EnrollFails) {
Jeff Pu84c60182023-12-04 16:53:04 +0000210 FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000211 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
212 mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
213 mCancel.get_future());
214 ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
215 ASSERT_EQ(0, FaceHalProperties::enrollments().size());
216}
217
218TEST_F(FakeFaceEngineTest, EnrollCancel) {
Jeff Pu84c60182023-12-04 16:53:04 +0000219 FaceHalProperties::next_enrollment("1:2000-[21,8,9],300:false");
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000220 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
221 mCancel.set_value();
222 mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
223 mCancel.get_future());
224 ASSERT_EQ(Error::CANCELED, mCallback->mError);
225 ASSERT_EQ(-1, mCallback->mLastEnrolled);
226 ASSERT_EQ(0, FaceHalProperties::enrollments().size());
Jeff Pu84c60182023-12-04 16:53:04 +0000227 ASSERT_TRUE(FaceHalProperties::next_enrollment().has_value());
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000228}
229
230TEST_F(FakeFaceEngineTest, Authenticate) {
231 FaceHalProperties::enrollments({100});
232 FaceHalProperties::enrollment_hit(100);
233 mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
234
235 ASSERT_EQ(100, mCallback->mLastAuthenticated);
236 ASSERT_FALSE(mCallback->mAuthenticateFailed);
237}
238
239TEST_F(FakeFaceEngineTest, AuthenticateCancel) {
240 FaceHalProperties::enrollments({100});
241 FaceHalProperties::enrollment_hit(100);
242 mCancel.set_value();
243 mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
244 ASSERT_EQ(Error::CANCELED, mCallback->mError);
245}
246
247TEST_F(FakeFaceEngineTest, AuthenticateFailedForUnEnrolled) {
248 FaceHalProperties::enrollments({3});
249 FaceHalProperties::enrollment_hit(100);
250 mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
Jeff Pu484d2e72023-09-25 15:11:19 +0000251 ASSERT_EQ(Error::TIMEOUT, mCallback->mError);
Joshua McCloskeydb009a52022-05-10 05:18:20 +0000252 ASSERT_TRUE(mCallback->mAuthenticateFailed);
253}
254
255TEST_F(FakeFaceEngineTest, DetectInteraction) {
256 FaceHalProperties::enrollments({100});
257 FaceHalProperties::enrollment_hit(100);
258 ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
259 mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
260 ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
261}
262
263TEST_F(FakeFaceEngineTest, DetectInteractionCancel) {
264 FaceHalProperties::enrollments({100});
265 FaceHalProperties::enrollment_hit(100);
266 mCancel.set_value();
267 mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
268 ASSERT_EQ(Error::CANCELED, mCallback->mError);
269}
270
271TEST_F(FakeFaceEngineTest, GetFeatureEmpty) {
272 mEngine.getFeaturesImpl(mCallback.get());
273 ASSERT_TRUE(mCallback->mFeatures.empty());
274}
275
276TEST_F(FakeFaceEngineTest, SetFeature) {
277 FaceHalProperties::enrollments({1});
278 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
279 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
280 auto features = mCallback->mFeatures;
281 ASSERT_TRUE(features.empty());
282 ASSERT_EQ(Feature::REQUIRE_ATTENTION, mCallback->mLastFeatureSet);
283
284 mEngine.getFeaturesImpl(mCallback.get());
285 features = mCallback->mFeatures;
286 ASSERT_FALSE(features.empty());
287 ASSERT_NE(features.end(),
288 std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
289}
290
291TEST_F(FakeFaceEngineTest, ToggleFeature) {
292 FaceHalProperties::enrollments({1});
293 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
294 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
295 mEngine.getFeaturesImpl(mCallback.get());
296 auto features = mCallback->mFeatures;
297 ASSERT_FALSE(features.empty());
298 ASSERT_NE(features.end(),
299 std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
300
301 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, false);
302 mEngine.getFeaturesImpl(mCallback.get());
303 features = mCallback->mFeatures;
304 ASSERT_TRUE(features.empty());
305}
306
307TEST_F(FakeFaceEngineTest, TurningOffNonExistentFeatureDoesNothing) {
308 FaceHalProperties::enrollments({1});
309 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
310 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, false);
311 mEngine.getFeaturesImpl(mCallback.get());
312 auto features = mCallback->mFeatures;
313 ASSERT_TRUE(features.empty());
314}
315
316TEST_F(FakeFaceEngineTest, SetMultipleFeatures) {
317 FaceHalProperties::enrollments({1});
318 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
319 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
320 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
321 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::DEBUG, true);
322 mEngine.getFeaturesImpl(mCallback.get());
323 auto features = mCallback->mFeatures;
324 ASSERT_EQ(3, features.size());
325 ASSERT_NE(features.end(),
326 std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
327 ASSERT_NE(features.end(),
328 std::find(features.begin(), features.end(), Feature::REQUIRE_DIVERSE_POSES));
329 ASSERT_NE(features.end(), std::find(features.begin(), features.end(), Feature::DEBUG));
330}
331
332TEST_F(FakeFaceEngineTest, SetMultipleFeaturesAndTurnOffSome) {
333 FaceHalProperties::enrollments({1});
334 keymaster::HardwareAuthToken hat{.mac = {2, 4}};
335 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
336 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
337 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::DEBUG, true);
338 mEngine.setFeatureImpl(mCallback.get(), hat, Feature::DEBUG, false);
339 mEngine.getFeaturesImpl(mCallback.get());
340 auto features = mCallback->mFeatures;
341 ASSERT_EQ(2, features.size());
342 ASSERT_NE(features.end(),
343 std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
344 ASSERT_NE(features.end(),
345 std::find(features.begin(), features.end(), Feature::REQUIRE_DIVERSE_POSES));
346 ASSERT_EQ(features.end(), std::find(features.begin(), features.end(), Feature::DEBUG));
347}
348
349TEST_F(FakeFaceEngineTest, Enumerate) {
350 FaceHalProperties::enrollments({120, 3});
351 mEngine.enumerateEnrollmentsImpl(mCallback.get());
352 auto enrolls = mCallback->mLastEnrollmentsEnumerated;
353 ASSERT_FALSE(enrolls.empty());
354 ASSERT_NE(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 120));
355 ASSERT_NE(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 3));
356}
357
358TEST_F(FakeFaceEngineTest, RemoveEnrollments) {
359 FaceHalProperties::enrollments({120, 3, 100});
360 mEngine.removeEnrollmentsImpl(mCallback.get(), {120, 100});
361 mEngine.enumerateEnrollmentsImpl(mCallback.get());
362 auto enrolls = mCallback->mLastEnrollmentsEnumerated;
363 ASSERT_FALSE(enrolls.empty());
364 ASSERT_EQ(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 120));
365 ASSERT_NE(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 3));
366 ASSERT_EQ(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 100));
367}
368
369TEST_F(FakeFaceEngineTest, ResetLockoutWithAuth) {
370 FaceHalProperties::lockout(true);
371 FaceHalProperties::enrollments({33});
372 FaceHalProperties::enrollment_hit(33);
373 auto cancelFuture = mCancel.get_future();
374 mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
375
376 ASSERT_TRUE(mCallback->mLockoutPermanent);
377
378 mEngine.resetLockoutImpl(mCallback.get(), {} /* hat */);
379 ASSERT_FALSE(mCallback->mLockoutPermanent);
380 FaceHalProperties::enrollment_hit(33);
381 mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
382 ASSERT_EQ(33, mCallback->mLastAuthenticated);
383 ASSERT_FALSE(mCallback->mAuthenticateFailed);
384}
385
Jeff Pu484d2e72023-09-25 15:11:19 +0000386} // namespace aidl::android::hardware::biometrics::face