Face Virtual HAL enrollment basic support via SUW
Bug: 294254230 Test: Manually perform face enrollment via Settings Change-Id: I30f6ffc3cde615b9cab55ef060623464a7799100
This commit is contained in:
parent
761095c9ac
commit
84c60185f1
3 changed files with 126 additions and 86 deletions
|
@ -53,15 +53,6 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
|
|||
const std::vector<Feature>& /*features*/,
|
||||
const std::future<void>& cancel) {
|
||||
BEGIN_OP(FaceHalProperties::operation_start_enroll_latency().value_or(0));
|
||||
// format is "<id>,<bucket_id>:<delay>:<succeeds>,<bucket_id>:<delay>:<succeeds>...
|
||||
auto nextEnroll = FaceHalProperties::next_enrollment().value_or("");
|
||||
// Erase the next enrollment
|
||||
FaceHalProperties::next_enrollment({});
|
||||
|
||||
AuthenticationFrame frame;
|
||||
frame.data.acquiredInfo = AcquiredInfo::START;
|
||||
frame.data.vendorCode = 0;
|
||||
cb->onAuthenticationFrame(frame);
|
||||
|
||||
// Do proper HAT verification in the real implementation.
|
||||
if (hat.mac.empty()) {
|
||||
|
@ -70,66 +61,81 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
|
|||
return;
|
||||
}
|
||||
|
||||
if (FaceHalProperties::operation_enroll_fails().value_or(false)) {
|
||||
LOG(ERROR) << "Fail: operation_enroll_fails";
|
||||
// Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
|
||||
// ------:-----------------------------------------:--------------
|
||||
// | | |--->enrollment success (true/false)
|
||||
// | |--> progress_steps
|
||||
// |
|
||||
// |-->enrollment id
|
||||
//
|
||||
//
|
||||
// progress_steps
|
||||
// <progress_duration>-[acquiredInfo,...]+
|
||||
// ---------------------------- ---------------------
|
||||
// | |-> sequence of acquiredInfo code
|
||||
// | --> time duration of the step in ms
|
||||
//
|
||||
// E.g. 1:2000-[21,1108,5,6,1],1000-[1113,4,1]:true
|
||||
// A success enrollement of id 1 by 2 steps
|
||||
// 1st step lasts 2000ms with acquiredInfo codes (21,1108,5,6,1)
|
||||
// 2nd step lasts 1000ms with acquiredInfo codes (1113,4,1)
|
||||
//
|
||||
std::string defaultNextEnrollment =
|
||||
"1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true";
|
||||
auto nextEnroll = FaceHalProperties::next_enrollment().value_or(defaultNextEnrollment);
|
||||
auto parts = Util::split(nextEnroll, ":");
|
||||
if (parts.size() != 3) {
|
||||
LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
auto parts = Util::split(nextEnroll, ",");
|
||||
if (parts.size() < 2) {
|
||||
LOG(ERROR) << "Fail: invalid next_enrollment for : " << nextEnroll;
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
auto enrollmentId = std::stoi(parts[0]);
|
||||
const int numBuckets = parts.size() - 1;
|
||||
for (size_t i = 1; i < parts.size(); i++) {
|
||||
auto enrollHit = Util::split(parts[i], ":");
|
||||
if (enrollHit.size() != 3) {
|
||||
LOG(ERROR) << "Error when unpacking enrollment hit: " << parts[i];
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
}
|
||||
std::string bucket = enrollHit[0];
|
||||
std::string delay = enrollHit[1];
|
||||
std::string succeeds = enrollHit[2];
|
||||
auto progress = Util::parseEnrollmentCapture(parts[1]);
|
||||
for (size_t i = 0; i < progress.size(); i += 2) {
|
||||
auto left = (progress.size() - i) / 2 - 1;
|
||||
auto duration = progress[i][0];
|
||||
auto acquired = progress[i + 1];
|
||||
auto N = acquired.size();
|
||||
|
||||
SLEEP_MS(std::stoi(delay));
|
||||
for (int j = 0; j < N; j++) {
|
||||
SLEEP_MS(duration / N);
|
||||
|
||||
if (shouldCancel(cancel)) {
|
||||
LOG(ERROR) << "Fail: cancel";
|
||||
cb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
return;
|
||||
if (shouldCancel(cancel)) {
|
||||
LOG(ERROR) << "Fail: cancel";
|
||||
cb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
return;
|
||||
}
|
||||
EnrollmentFrame frame = {};
|
||||
auto ac = convertAcquiredInfo(acquired[j]);
|
||||
frame.data.acquiredInfo = ac.first;
|
||||
frame.data.vendorCode = ac.second;
|
||||
frame.stage = (i == 0 && j == 0) ? EnrollmentStage::FIRST_FRAME_RECEIVED
|
||||
: (i == progress.size() - 2 && j == N - 1)
|
||||
? EnrollmentStage::ENROLLMENT_FINISHED
|
||||
: EnrollmentStage::WAITING_FOR_CENTERING;
|
||||
cb->onEnrollmentFrame(frame);
|
||||
}
|
||||
|
||||
if (!IS_TRUE(succeeds)) { // end and failed
|
||||
LOG(ERROR) << "Fail: requested by caller: " << parts[i];
|
||||
if (left == 0 && !IS_TRUE(parts[2])) { // end and failed
|
||||
LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
|
||||
FaceHalProperties::next_enrollment({});
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
|
||||
return;
|
||||
}
|
||||
|
||||
EnrollmentFrame frame;
|
||||
|
||||
frame.data.acquiredInfo = AcquiredInfo::GOOD;
|
||||
frame.data.vendorCode = 0;
|
||||
cb->onEnrollmentFrame(frame);
|
||||
|
||||
frame.data.acquiredInfo = AcquiredInfo::VENDOR;
|
||||
frame.data.vendorCode = std::stoi(bucket);
|
||||
cb->onEnrollmentFrame(frame);
|
||||
|
||||
int remainingBuckets = numBuckets - i;
|
||||
if (remainingBuckets > 0) {
|
||||
cb->onEnrollmentProgress(enrollmentId, remainingBuckets);
|
||||
} else { // progress and update props if last time
|
||||
LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left;
|
||||
if (left == 0) {
|
||||
auto enrollments = FaceHalProperties::enrollments();
|
||||
enrollments.emplace_back(enrollmentId);
|
||||
FaceHalProperties::enrollments(enrollments);
|
||||
FaceHalProperties::next_enrollment({});
|
||||
// change authenticatorId after new enrollment
|
||||
auto id = FaceHalProperties::authenticator_id().value_or(0);
|
||||
auto newId = id + 1;
|
||||
FaceHalProperties::authenticator_id(newId);
|
||||
LOG(INFO) << "Enrolled: " << enrollmentId;
|
||||
}
|
||||
cb->onEnrollmentProgress(enrollmentId, left);
|
||||
}
|
||||
}
|
||||
|
||||
auto enrollments = FaceHalProperties::enrollments();
|
||||
enrollments.push_back(enrollmentId);
|
||||
FaceHalProperties::enrollments(enrollments);
|
||||
LOG(INFO) << "enrolled : " << enrollmentId;
|
||||
cb->onEnrollmentProgress(enrollmentId, 0);
|
||||
}
|
||||
|
||||
void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/,
|
||||
|
|
|
@ -1,30 +1,35 @@
|
|||
# Face Virtual HAL (VHAL)
|
||||
|
||||
This is a virtual HAL implementation that is backed by system properties
|
||||
instead of actual hardware. It's intended for testing and UI development
|
||||
on debuggable builds to allow devices to masquerade as alternative device
|
||||
types and for emulators.
|
||||
Note: The virtual face HAL feature development will be done in phases. Refer to this doc often for
|
||||
the latest supported features
|
||||
This is a virtual HAL implementation that is backed by system properties instead
|
||||
of actual hardware. It's intended for testing and UI development on debuggable
|
||||
builds to allow devices to masquerade as alternative device types and for
|
||||
emulators. Note: The virtual face HAL feature development will be done in
|
||||
phases. Refer to this doc often for the latest supported features
|
||||
|
||||
## Supported Devices
|
||||
|
||||
The face virtual hal is automatically built in in all debug builds (userdebug and eng) for the latest pixel devices and CF.
|
||||
The instructions in this doc applies to all
|
||||
The face virtual hal is automatically built in in all debug builds (userdebug<br/>
|
||||
and eng) for the latest pixel devices and CF. The instructions in this doc<br/>
|
||||
applies to all
|
||||
|
||||
## Enabling Face Virtual HAL
|
||||
|
||||
On pixel devicse (non-CF), by default (after manufacture reset), Face VHAL is not enabled. Therefore real Face HAL is used.
|
||||
Face VHAL enabling is gated by the following two AND conditions:
|
||||
1. The Face VHAL feature flag (as part of Trunk-development strategy) must be tured until the flags life-cycle ends.
|
||||
2. The Face VHAL must be enabled via sysprop
|
||||
See adb commands below
|
||||
On pixel devicse (non-CF), by default (after manufacture reset), Face VHAL is <br/>
|
||||
not enabled. Therefore real Face HAL is used. Face VHAL enabling is gated by the<br/>
|
||||
following two AND conditions:<br/>
|
||||
1. The Face VHAL feature flag (as part ofTrunk-development strategy) must be<br/>
|
||||
turned on until the flags life-cycle ends.
|
||||
2. The Face VHAL must be enabled via sysprop.
|
||||
|
||||
##Getting Stared
|
||||
See the adb commands below
|
||||
|
||||
A basic use case for a successful authentication via Face VHAL is given as an exmple below.
|
||||
## Getting Stared
|
||||
|
||||
A basic use case for a successful authentication via Face VHAL is given as an
|
||||
exmple below.
|
||||
|
||||
### Enabling VHAL
|
||||
|
||||
```shell
|
||||
$ adb root
|
||||
$ adb shell device_config put biometrics_framework com.android.server.biometrics.face_vhal_feature true
|
||||
|
@ -35,6 +40,7 @@ $ adb reboot
|
|||
```
|
||||
|
||||
### Direct Enrollment
|
||||
|
||||
```shell
|
||||
$ adb shell locksettings set-pin 0000
|
||||
$ adb shell setprop persist.vendor.face.virtual.enrollments 1
|
||||
|
@ -42,22 +48,47 @@ $ adb shell cmd face syncadb shell cmd face sync
|
|||
```
|
||||
|
||||
## Authenticating
|
||||
To authenticate successfully, the captured (hit) must match the enrollment id set above. To trigger
|
||||
authentication failure, set the hit id to a different value.
|
||||
|
||||
To authenticate successfully, the captured (hit) must match the enrollment id<br/>
|
||||
set above. To trigger authentication failure, set the hit id to a different value.
|
||||
```shell
|
||||
$ adb shell setprop vendor.face.virtual.operation_authenticate_duration 800
|
||||
$ adb shell setprop vendor.face.virtual.enrollment_hit 1
|
||||
```
|
||||
Refer to face.sysprop for full supported features of authentication, such as error and acquiredInfo insertion
|
||||
|
||||
### AcquiredInfo
|
||||
AcquiredInfo codes can be sent during authentication by specifying the sysprop.<br/>
|
||||
The codes is sent in sequence and in the interval of operation_authentication_duration/numberOfAcquiredInfoCode
|
||||
```shell
|
||||
$ adb shell setprop vendor.face.virtual.operation_authenticate_acquired 6,9,1013
|
||||
```
|
||||
Refer to [AcquiredInfo.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl) for full face acquiredInfo codes.
|
||||
Note: For vendor specific acquired info, acquiredInfo = 1000 + vendorCode.
|
||||
|
||||
### Error Insertion
|
||||
Error can be inserted during authentction by specifying the authenticate_error sysprop.
|
||||
```shell
|
||||
$ adb shell setprop vendor.face.virtual.operation_authenticate_error 4
|
||||
```
|
||||
Refer to [Error.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/Error.aidl) for full face error codes
|
||||
|
||||
|
||||
## Enrollment via Setup
|
||||
## Enrollment via Settings
|
||||
|
||||
Enrollment process is specified by sysprop `next_enrollment` in the following format
|
||||
|
||||
```shell
|
||||
# authenticar_id,bucket_id:duration:(true|false)....
|
||||
$ adb shell setprop vendor.face.virtual.next_enrollment 1,0:500:true,5:250:true,10:150:true,15:500:true
|
||||
$ walk thru the manual enrollment process by following screen instructions
|
||||
Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
|
||||
----:-----------------------------------:---------
|
||||
| | |--->sucess (true/false)
|
||||
| |--> progress_step(s)
|
||||
|
|
||||
|-->enrollment_id
|
||||
|
||||
# If you would like to get rid of the enrollment, run the follwoing command
|
||||
$ adb shell setprop persist.vendor.face.virtual.enrollments \"\"
|
||||
E.g.
|
||||
$ adb shell setprop vendor.face.virtual.next_enrollment 1:6000-[21,8,1,1108,1,10,1113,1,1118,1124]:true
|
||||
```
|
||||
If next_enrollment prop is not set, the following default value is used:<br/>
|
||||
defaultNextEnrollment="1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true"<br/>
|
||||
Note: Enrollment data and configuration can be supported upon request in case of needs
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ class TestSessionCallback : public BnSessionCallback {
|
|||
};
|
||||
::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
|
||||
if (remaining == 0) mLastEnrolled = enrollmentId;
|
||||
mRemaining = remaining;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
|
||||
|
@ -128,6 +129,7 @@ class TestSessionCallback : public BnSessionCallback {
|
|||
bool mAuthenticatorIdInvalidated = false;
|
||||
bool mLockoutPermanent = false;
|
||||
int mInteractionDetectedCount = 0;
|
||||
int mRemaining = -1;
|
||||
};
|
||||
|
||||
class FakeFaceEngineTest : public ::testing::Test {
|
||||
|
@ -193,7 +195,7 @@ TEST_F(FakeFaceEngineTest, AuthenticatorIdInvalidate) {
|
|||
}
|
||||
|
||||
TEST_F(FakeFaceEngineTest, Enroll) {
|
||||
FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:true");
|
||||
FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
|
||||
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
|
||||
mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
|
||||
mCancel.get_future());
|
||||
|
@ -201,10 +203,11 @@ TEST_F(FakeFaceEngineTest, Enroll) {
|
|||
ASSERT_EQ(1, FaceHalProperties::enrollments().size());
|
||||
ASSERT_EQ(1, FaceHalProperties::enrollments()[0].value());
|
||||
ASSERT_EQ(1, mCallback->mLastEnrolled);
|
||||
ASSERT_EQ(0, mCallback->mRemaining);
|
||||
}
|
||||
|
||||
TEST_F(FakeFaceEngineTest, EnrollFails) {
|
||||
FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:false");
|
||||
FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
|
||||
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
|
||||
mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
|
||||
mCancel.get_future());
|
||||
|
@ -213,7 +216,7 @@ TEST_F(FakeFaceEngineTest, EnrollFails) {
|
|||
}
|
||||
|
||||
TEST_F(FakeFaceEngineTest, EnrollCancel) {
|
||||
FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:false");
|
||||
FaceHalProperties::next_enrollment("1:2000-[21,8,9],300:false");
|
||||
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
|
||||
mCancel.set_value();
|
||||
mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
|
||||
|
@ -221,7 +224,7 @@ TEST_F(FakeFaceEngineTest, EnrollCancel) {
|
|||
ASSERT_EQ(Error::CANCELED, mCallback->mError);
|
||||
ASSERT_EQ(-1, mCallback->mLastEnrolled);
|
||||
ASSERT_EQ(0, FaceHalProperties::enrollments().size());
|
||||
ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
|
||||
ASSERT_TRUE(FaceHalProperties::next_enrollment().has_value());
|
||||
}
|
||||
|
||||
TEST_F(FakeFaceEngineTest, Authenticate) {
|
||||
|
|
Loading…
Reference in a new issue