1 /*
2 * Copyright (C) 2019 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 "biometrics_face_hidl_hal_test"
18
19 #include <android/hardware/biometrics/face/1.0/IBiometricsFace.h>
20 #include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h>
21
22 #include <VtsHalHidlTargetCallbackBase.h>
23 #include <VtsHalHidlTargetTestBase.h>
24 #include <VtsHalHidlTargetTestEnvBase.h>
25 #include <android-base/logging.h>
26
27 #include <chrono>
28 #include <cstdint>
29 #include <random>
30
31 using android::sp;
32 using android::hardware::hidl_vec;
33 using android::hardware::Return;
34 using android::hardware::Void;
35 using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo;
36 using android::hardware::biometrics::face::V1_0::FaceError;
37 using android::hardware::biometrics::face::V1_0::Feature;
38 using android::hardware::biometrics::face::V1_0::IBiometricsFace;
39 using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
40 using android::hardware::biometrics::face::V1_0::OptionalBool;
41 using android::hardware::biometrics::face::V1_0::OptionalUint64;
42 using android::hardware::biometrics::face::V1_0::Status;
43
44 namespace {
45
46 // Arbitrary, nonexistent userId
47 constexpr uint32_t kUserId = 9;
48 // Arbitrary, nonexistent faceId
49 constexpr uint32_t kFaceId = 5;
50 constexpr uint32_t kTimeoutSec = 3;
51 constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
52 constexpr int kGenerateChallengeIterations = 10;
53 constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata";
54 constexpr char kCallbackNameOnEnrollResult[] = "onEnrollResult";
55 constexpr char kCallbackNameOnAuthenticated[] = "onAuthenticated";
56 constexpr char kCallbackNameOnAcquired[] = "onAcquired";
57 constexpr char kCallbackNameOnError[] = "onError";
58 constexpr char kCallbackNameOnRemoved[] = "onRemoved";
59 constexpr char kCallbackNameOnEnumerate[] = "onEnumerate";
60 constexpr char kCallbackNameOnLockoutChanged[] = "onLockoutChanged";
61
62 // Callback arguments that need to be captured for the tests.
63 struct FaceCallbackArgs {
64 // The error passed to the last onError() callback.
65 FaceError error;
66
67 // The userId passed to the last onRemoved() callback.
68 int32_t userId;
69 };
70
71 // Test callback class for the BiometricsFace HAL.
72 // The HAL will call these callback methods to notify about completed operations
73 // or encountered errors.
74 class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>,
75 public IBiometricsFaceClientCallback {
76 public:
onEnrollResult(uint64_t,uint32_t,int32_t,uint32_t)77 Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override {
78 NotifyFromCallback(kCallbackNameOnEnrollResult);
79 return Void();
80 }
81
onAuthenticated(uint64_t,uint32_t,int32_t,const hidl_vec<uint8_t> &)82 Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override {
83 NotifyFromCallback(kCallbackNameOnAuthenticated);
84 return Void();
85 }
86
onAcquired(uint64_t,int32_t,FaceAcquiredInfo,int32_t)87 Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override {
88 NotifyFromCallback(kCallbackNameOnAcquired);
89 return Void();
90 }
91
onError(uint64_t,int32_t,FaceError error,int32_t)92 Return<void> onError(uint64_t, int32_t, FaceError error, int32_t) override {
93 FaceCallbackArgs args = {};
94 args.error = error;
95 NotifyFromCallback(kCallbackNameOnError, args);
96 return Void();
97 }
98
onRemoved(uint64_t,const hidl_vec<uint32_t> &,int32_t userId)99 Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t userId) override {
100 FaceCallbackArgs args = {};
101 args.userId = userId;
102 NotifyFromCallback(kCallbackNameOnRemoved, args);
103 return Void();
104 }
105
onEnumerate(uint64_t,const hidl_vec<uint32_t> &,int32_t)106 Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override {
107 NotifyFromCallback(kCallbackNameOnEnumerate);
108 return Void();
109 }
110
onLockoutChanged(uint64_t)111 Return<void> onLockoutChanged(uint64_t) override {
112 NotifyFromCallback(kCallbackNameOnLockoutChanged);
113 return Void();
114 }
115 };
116
117 // Test environment for the BiometricsFace HAL.
118 class FaceHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
119 public:
120 // Get the test environment singleton.
Instance()121 static FaceHidlEnvironment* Instance() {
122 static FaceHidlEnvironment* instance = new FaceHidlEnvironment;
123 return instance;
124 }
125
registerTestServices()126 void registerTestServices() override { registerTestService<IBiometricsFace>(); }
127
128 private:
129 FaceHidlEnvironment() = default;
130 };
131
132 // Test class for the BiometricsFace HAL.
133 class FaceHidlTest : public ::testing::VtsHalHidlTargetTestBase {
134 public:
SetUp()135 void SetUp() override {
136 mService = ::testing::VtsHalHidlTargetTestBase::getService<IBiometricsFace>(
137 FaceHidlEnvironment::Instance()->getServiceName<IBiometricsFace>());
138 ASSERT_NE(mService, nullptr);
139 mCallback = new FaceCallback();
140 mCallback->SetWaitTimeoutDefault(kTimeout);
141 Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) {
142 ASSERT_EQ(Status::OK, res.status);
143 // Makes sure the "deviceId" represented by "res.value" is not 0.
144 // 0 would mean the HIDL is not available.
145 ASSERT_NE(0UL, res.value);
146 });
147 ASSERT_TRUE(ret1.isOk());
148 Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir);
149 ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
150 }
151
TearDown()152 void TearDown() override {}
153
154 sp<IBiometricsFace> mService;
155 sp<FaceCallback> mCallback;
156 };
157
158 // generateChallenge should always return a unique, cryptographically secure,
159 // non-zero number.
TEST_F(FaceHidlTest,GenerateChallengeTest)160 TEST_F(FaceHidlTest, GenerateChallengeTest) {
161 std::map<uint64_t, int> m;
162 for (int i = 0; i < kGenerateChallengeIterations; ++i) {
163 Return<void> ret =
164 mService->generateChallenge(kTimeoutSec, [&m](const OptionalUint64& res) {
165 ASSERT_EQ(Status::OK, res.status);
166 EXPECT_NE(0UL, res.value);
167 m[res.value]++;
168 EXPECT_EQ(1UL, m[res.value]);
169 });
170 ASSERT_TRUE(ret.isOk());
171 }
172 }
173
174 // enroll with an invalid (all zeroes) HAT should fail.
TEST_F(FaceHidlTest,EnrollZeroHatTest)175 TEST_F(FaceHidlTest, EnrollZeroHatTest) {
176 // Filling HAT with zeros
177 hidl_vec<uint8_t> token(69);
178 for (size_t i = 0; i < 69; i++) {
179 token[i] = 0;
180 }
181
182 Return<Status> ret = mService->enroll(token, kTimeoutSec, {});
183 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
184
185 // onError should be called with a meaningful (nonzero) error.
186 auto res = mCallback->WaitForCallback(kCallbackNameOnError);
187 EXPECT_TRUE(res.no_timeout);
188 EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
189 }
190
191 // enroll with an invalid HAT should fail.
TEST_F(FaceHidlTest,EnrollGarbageHatTest)192 TEST_F(FaceHidlTest, EnrollGarbageHatTest) {
193 // Filling HAT with pseudorandom invalid data.
194 // Using default seed to make the test reproducible.
195 std::mt19937 gen(std::mt19937::default_seed);
196 std::uniform_int_distribution<uint8_t> dist;
197 hidl_vec<uint8_t> token(69);
198 for (size_t i = 0; i < 69; ++i) {
199 token[i] = dist(gen);
200 }
201
202 Return<Status> ret = mService->enroll(token, kTimeoutSec, {});
203 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
204
205 // onError should be called with a meaningful (nonzero) error.
206 auto res = mCallback->WaitForCallback(kCallbackNameOnError);
207 EXPECT_TRUE(res.no_timeout);
208 EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
209 }
210
211 // setFeature with an invalid (all zeros) HAT should fail.
TEST_F(FaceHidlTest,SetFeatureZeroHatTest)212 TEST_F(FaceHidlTest, SetFeatureZeroHatTest) {
213 hidl_vec<uint8_t> token(69);
214 for (size_t i = 0; i < 69; i++) {
215 token[i] = 0;
216 }
217
218 Return<Status> ret = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token, 0);
219 ASSERT_EQ(Status::ILLEGAL_ARGUMENT, static_cast<Status>(ret));
220 }
221
222 // setFeature with an invalid HAT should fail.
TEST_F(FaceHidlTest,SetFeatureGarbageHatTest)223 TEST_F(FaceHidlTest, SetFeatureGarbageHatTest) {
224 // Filling HAT with pseudorandom invalid data.
225 // Using default seed to make the test reproducible.
226 std::mt19937 gen(std::mt19937::default_seed);
227 std::uniform_int_distribution<uint8_t> dist;
228 hidl_vec<uint8_t> token(69);
229 for (size_t i = 0; i < 69; ++i) {
230 token[i] = dist(gen);
231 }
232
233 Return<Status> ret = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token, 0);
234 ASSERT_EQ(Status::ILLEGAL_ARGUMENT, static_cast<Status>(ret));
235 }
236
assertGetFeatureFails(const sp<IBiometricsFace> & service,uint32_t faceId,Feature feature)237 void assertGetFeatureFails(const sp<IBiometricsFace>& service, uint32_t faceId, Feature feature) {
238 // Features cannot be retrieved for invalid faces.
239 Return<void> res = service->getFeature(feature, faceId, [](const OptionalBool& result) {
240 ASSERT_EQ(Status::ILLEGAL_ARGUMENT, result.status);
241 });
242 ASSERT_TRUE(res.isOk());
243 }
244
TEST_F(FaceHidlTest,GetFeatureRequireAttentionTest)245 TEST_F(FaceHidlTest, GetFeatureRequireAttentionTest) {
246 assertGetFeatureFails(mService, 0 /* faceId */, Feature::REQUIRE_ATTENTION);
247 }
248
TEST_F(FaceHidlTest,GetFeatureRequireDiversityTest)249 TEST_F(FaceHidlTest, GetFeatureRequireDiversityTest) {
250 assertGetFeatureFails(mService, 0 /* faceId */, Feature::REQUIRE_DIVERSITY);
251 }
252
253 // revokeChallenge should always return within the timeout
TEST_F(FaceHidlTest,RevokeChallengeTest)254 TEST_F(FaceHidlTest, RevokeChallengeTest) {
255 auto start = std::chrono::system_clock::now();
256 Return<Status> ret = mService->revokeChallenge();
257 auto elapsed = std::chrono::system_clock::now() - start;
258 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
259 ASSERT_GE(kTimeout, elapsed);
260 }
261
262 // The call to getAuthenticatorId should succeed.
TEST_F(FaceHidlTest,GetAuthenticatorIdTest)263 TEST_F(FaceHidlTest, GetAuthenticatorIdTest) {
264 Return<void> ret = mService->getAuthenticatorId(
265 [](const OptionalUint64& res) { ASSERT_EQ(Status::OK, res.status); });
266 ASSERT_TRUE(ret.isOk());
267 }
268
269 // The call to enumerate should succeed.
TEST_F(FaceHidlTest,EnumerateTest)270 TEST_F(FaceHidlTest, EnumerateTest) {
271 Return<Status> ret = mService->enumerate();
272 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
273 auto res = mCallback->WaitForCallback(kCallbackNameOnEnumerate);
274 EXPECT_TRUE(res.no_timeout);
275 }
276
277 // The call to remove should succeed for any faceId
TEST_F(FaceHidlTest,RemoveFaceTest)278 TEST_F(FaceHidlTest, RemoveFaceTest) {
279 // Remove a face
280 Return<Status> ret = mService->remove(kFaceId);
281 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
282 }
283
284 // Remove should accept 0 to delete all faces
TEST_F(FaceHidlTest,RemoveAllFacesTest)285 TEST_F(FaceHidlTest, RemoveAllFacesTest) {
286 // Remove all faces
287 Return<Status> ret = mService->remove(0);
288 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
289 }
290
291 // Active user should successfully set to a writable location.
TEST_F(FaceHidlTest,SetActiveUserTest)292 TEST_F(FaceHidlTest, SetActiveUserTest) {
293 // Create an active user
294 Return<Status> ret = mService->setActiveUser(2, kFacedataDir);
295 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
296
297 // Reset active user
298 ret = mService->setActiveUser(kUserId, kFacedataDir);
299 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
300 }
301
302 // Active user should fail to set to an unwritable location.
TEST_F(FaceHidlTest,SetActiveUserUnwritableTest)303 TEST_F(FaceHidlTest, SetActiveUserUnwritableTest) {
304 // Create an active user to an unwritable location (device root dir)
305 Return<Status> ret = mService->setActiveUser(3, "/");
306 ASSERT_NE(Status::OK, static_cast<Status>(ret));
307
308 // Reset active user
309 ret = mService->setActiveUser(kUserId, kFacedataDir);
310 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
311 }
312
313 // Active user should fail to set to a null location.
TEST_F(FaceHidlTest,SetActiveUserNullTest)314 TEST_F(FaceHidlTest, SetActiveUserNullTest) {
315 // Create an active user to a null location.
316 Return<Status> ret = mService->setActiveUser(4, nullptr);
317 ASSERT_NE(Status::OK, static_cast<Status>(ret));
318
319 // Reset active user
320 ret = mService->setActiveUser(kUserId, kFacedataDir);
321 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
322 }
323
324 // Cancel should always return CANCELED from any starting state including
325 // the IDLE state.
TEST_F(FaceHidlTest,CancelTest)326 TEST_F(FaceHidlTest, CancelTest) {
327 Return<Status> ret = mService->cancel();
328 // check that we were able to make an IPC request successfully
329 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
330 auto res = mCallback->WaitForCallback(kCallbackNameOnError);
331 // make sure callback was invoked within kRevokeChallengeTimeout
332 EXPECT_TRUE(res.no_timeout);
333 EXPECT_EQ(FaceError::CANCELED, res.args->error);
334 }
335
TEST_F(FaceHidlTest,OnLockoutChangedTest)336 TEST_F(FaceHidlTest, OnLockoutChangedTest) {
337 // Update active user and ensure onLockoutChanged was called.
338 Return<Status> ret = mService->setActiveUser(kUserId + 1, kFacedataDir);
339 ASSERT_EQ(Status::OK, static_cast<Status>(ret));
340
341 // Make sure callback was invoked
342 auto res = mCallback->WaitForCallback(kCallbackNameOnLockoutChanged);
343 EXPECT_TRUE(res.no_timeout);
344 }
345
346 } // anonymous namespace
347
main(int argc,char ** argv)348 int main(int argc, char** argv) {
349 ::testing::AddGlobalTestEnvironment(FaceHidlEnvironment::Instance());
350 ::testing::InitGoogleTest(&argc, argv);
351 FaceHidlEnvironment::Instance()->init(&argc, argv);
352 int status = RUN_ALL_TESTS();
353 LOG(INFO) << "Test result = " << status;
354 return status;
355 }
356