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