mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge ab/7633965
Bug: 169893837 Merged-In: I07cf1825c23e2467852d5cd93436eb9dee23b408 Change-Id: Ie9a09f85d7b24b031cb6869429a33ff1eeb84687
This commit is contained in:
@@ -332,18 +332,21 @@ TEST_P(AudioPrimaryHidlTest, setMode) {
|
||||
#endif
|
||||
|
||||
for (int mode : {-2, -1, maxMode + 1}) {
|
||||
ASSERT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(AudioMode(mode)))
|
||||
EXPECT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(AudioMode(mode)))
|
||||
<< "mode=" << mode;
|
||||
}
|
||||
// Test valid values
|
||||
for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE,
|
||||
AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) {
|
||||
ASSERT_OK(getDevice()->setMode(mode)) << "mode=" << toString(mode);
|
||||
}
|
||||
|
||||
// AudioMode::CALL_SCREEN as support is optional
|
||||
#if MAJOR_VERSION >= 6
|
||||
ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setMode(AudioMode::CALL_SCREEN));
|
||||
EXPECT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setMode(AudioMode::CALL_SCREEN));
|
||||
#endif
|
||||
// Test valid values
|
||||
for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE,
|
||||
AudioMode::NORMAL}) {
|
||||
EXPECT_OK(getDevice()->setMode(mode)) << "mode=" << toString(mode);
|
||||
}
|
||||
// Make sure to leave the test in normal mode
|
||||
getDevice()->setMode(AudioMode::NORMAL);
|
||||
}
|
||||
|
||||
TEST_P(AudioPrimaryHidlTest, setBtHfpSampleRate) {
|
||||
|
||||
@@ -2269,48 +2269,74 @@ TEST_P(EvsHidlTest, CameraStreamExternalBuffering) {
|
||||
|
||||
// Acquire the graphics buffer allocator
|
||||
android::GraphicBufferAllocator& alloc(android::GraphicBufferAllocator::get());
|
||||
const auto usage = GRALLOC_USAGE_HW_TEXTURE |
|
||||
GRALLOC_USAGE_SW_READ_RARELY |
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN;
|
||||
const auto usage =
|
||||
GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN;
|
||||
const auto format = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
const auto width = 640;
|
||||
const auto height = 360;
|
||||
|
||||
// Allocate buffers to use
|
||||
hidl_vec<BufferDesc> buffers;
|
||||
buffers.resize(kBuffersToHold);
|
||||
for (auto i = 0; i < kBuffersToHold; ++i) {
|
||||
unsigned pixelsPerLine;
|
||||
buffer_handle_t memHandle = nullptr;
|
||||
android::status_t result = alloc.allocate(width,
|
||||
height,
|
||||
format,
|
||||
1,
|
||||
usage,
|
||||
&memHandle,
|
||||
&pixelsPerLine,
|
||||
0,
|
||||
"EvsApp");
|
||||
if (result != android::NO_ERROR) {
|
||||
LOG(ERROR) << __FUNCTION__ << " failed to allocate memory.";
|
||||
} else {
|
||||
BufferDesc buf;
|
||||
AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<AHardwareBuffer_Desc *>(&buf.buffer.description);
|
||||
pDesc->width = width;
|
||||
pDesc->height = height;
|
||||
pDesc->layers = 1;
|
||||
pDesc->format = format;
|
||||
pDesc->usage = usage;
|
||||
pDesc->stride = pixelsPerLine;
|
||||
buf.buffer.nativeHandle = memHandle;
|
||||
buf.bufferId = i; // Unique number to identify this buffer
|
||||
buffers[i] = buf;
|
||||
}
|
||||
}
|
||||
uint32_t width = 640;
|
||||
uint32_t height = 360;
|
||||
camera_metadata_entry_t streamCfgs;
|
||||
|
||||
// Test each reported camera
|
||||
for (auto&& cam: cameraInfo) {
|
||||
for (auto&& cam : cameraInfo) {
|
||||
bool foundCfg = false;
|
||||
if (!find_camera_metadata_entry(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()),
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
|
||||
&streamCfgs)) {
|
||||
// Stream configurations are found in metadata
|
||||
RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
|
||||
|
||||
LOG(DEBUG) << __LINE__ << " start searching " << streamCfgs.count;
|
||||
for (unsigned idx = 0; idx < streamCfgs.count; idx++) {
|
||||
LOG(DEBUG) << "ptr->direction= " << ptr->direction
|
||||
<< " ptr->format= " << ptr->format;
|
||||
if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
|
||||
ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
|
||||
width = ptr->width;
|
||||
height = ptr->height;
|
||||
foundCfg = true;
|
||||
// Always use the 1st available configuration
|
||||
break;
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundCfg) {
|
||||
LOG(INFO) << "No configuration found. Use default stream configurations.";
|
||||
}
|
||||
|
||||
// Allocate buffers to use
|
||||
hidl_vec<BufferDesc> buffers;
|
||||
buffers.resize(kBuffersToHold);
|
||||
for (auto i = 0; i < kBuffersToHold; ++i) {
|
||||
unsigned pixelsPerLine;
|
||||
buffer_handle_t memHandle = nullptr;
|
||||
android::status_t result =
|
||||
alloc.allocate(width, height, format, 1, usage, &memHandle, &pixelsPerLine, 0,
|
||||
"CameraStreamExternalBufferingTest");
|
||||
if (result != android::NO_ERROR) {
|
||||
LOG(ERROR) << __FUNCTION__ << " failed to allocate memory.";
|
||||
// Release previous allocated buffers
|
||||
for (auto j = 0; j < i; j++) {
|
||||
alloc.free(buffers[i].buffer.nativeHandle);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
BufferDesc buf;
|
||||
AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<AHardwareBuffer_Desc*>(&buf.buffer.description);
|
||||
pDesc->width = width;
|
||||
pDesc->height = height;
|
||||
pDesc->layers = 1;
|
||||
pDesc->format = format;
|
||||
pDesc->usage = usage;
|
||||
pDesc->stride = pixelsPerLine;
|
||||
buf.buffer.nativeHandle = memHandle;
|
||||
buf.bufferId = i; // Unique number to identify this buffer
|
||||
buffers[i] = buf;
|
||||
}
|
||||
}
|
||||
|
||||
bool isLogicalCam = false;
|
||||
getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
|
||||
|
||||
@@ -2374,13 +2400,12 @@ TEST_P(EvsHidlTest, CameraStreamExternalBuffering) {
|
||||
// Explicitly release the camera
|
||||
pEnumerator->closeCamera(pCam);
|
||||
activeCameras.clear();
|
||||
// Release buffers
|
||||
for (auto& b : buffers) {
|
||||
alloc.free(b.buffer.nativeHandle);
|
||||
}
|
||||
buffers.resize(0);
|
||||
}
|
||||
|
||||
// Release buffers
|
||||
for (auto& b : buffers) {
|
||||
alloc.free(b.buffer.nativeHandle);
|
||||
}
|
||||
buffers.resize(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::STATIC,
|
||||
},
|
||||
.initialValue = {.floatValues = {1776, 4950, 2008, 2140, 2984, 1665, 1667, 11800}}},
|
||||
.initialValue = {.int32Values = {1776, 4950, 2008, 2140, 2984, 1665, 1667, 11800}}},
|
||||
{.config =
|
||||
{
|
||||
.prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED),
|
||||
@@ -174,7 +174,7 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.prop = toInt(VehicleProperty::PERF_ODOMETER),
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::CONTINUOUS,
|
||||
.minSampleRate = 0.0f,
|
||||
.minSampleRate = 1.0f,
|
||||
.maxSampleRate = 10.0f,
|
||||
},
|
||||
.initialValue = {.floatValues = {0.0f}}},
|
||||
@@ -183,7 +183,7 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.prop = toInt(VehicleProperty::PERF_STEERING_ANGLE),
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::CONTINUOUS,
|
||||
.minSampleRate = 0.0f,
|
||||
.minSampleRate = 1.0f,
|
||||
.maxSampleRate = 10.0f,
|
||||
},
|
||||
.initialValue = {.floatValues = {0.0f}}},
|
||||
@@ -192,7 +192,7 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.prop = toInt(VehicleProperty::PERF_REAR_STEERING_ANGLE),
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::CONTINUOUS,
|
||||
.minSampleRate = 0.0f,
|
||||
.minSampleRate = 1.0f,
|
||||
.maxSampleRate = 10.0f,
|
||||
},
|
||||
.initialValue = {.floatValues = {0.0f}}},
|
||||
@@ -213,7 +213,7 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.prop = toInt(VehicleProperty::FUEL_LEVEL),
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::CONTINUOUS,
|
||||
.minSampleRate = 0.0f,
|
||||
.minSampleRate = 1.0f,
|
||||
.maxSampleRate = 100.0f,
|
||||
},
|
||||
.initialValue = {.floatValues = {15000.0f}}},
|
||||
@@ -231,7 +231,7 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.prop = toInt(VehicleProperty::EV_BATTERY_LEVEL),
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::CONTINUOUS,
|
||||
.minSampleRate = 0.0f,
|
||||
.minSampleRate = 1.0f,
|
||||
.maxSampleRate = 100.0f,
|
||||
},
|
||||
.initialValue = {.floatValues = {150000.0f}}},
|
||||
@@ -328,6 +328,11 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.prop = toInt(VehicleProperty::CURRENT_GEAR),
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
|
||||
.configArray = {(int)VehicleGear::GEAR_PARK,
|
||||
(int)VehicleGear::GEAR_NEUTRAL,
|
||||
(int)VehicleGear::GEAR_REVERSE, (int)VehicleGear::GEAR_1,
|
||||
(int)VehicleGear::GEAR_2, (int)VehicleGear::GEAR_3,
|
||||
(int)VehicleGear::GEAR_4, (int)VehicleGear::GEAR_5},
|
||||
},
|
||||
.initialValue = {.int32Values = {toInt(VehicleGear::GEAR_PARK)}}},
|
||||
|
||||
@@ -1072,8 +1077,8 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.access = VehiclePropertyAccess::READ,
|
||||
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
|
||||
},
|
||||
.initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */,
|
||||
-1, -1, -1, -1 /* Insets */}},
|
||||
.initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1,
|
||||
-1, -1 /* Insets */}},
|
||||
},
|
||||
{
|
||||
.config =
|
||||
@@ -1126,9 +1131,9 @@ const ConfigDeclaration kVehicleProperties[]{
|
||||
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
|
||||
.configArray = {0, 0, 0, 11, 0, 0, 0, 0, 16},
|
||||
},
|
||||
.initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */,
|
||||
-1, -1, -1, -1 /* Insets */,
|
||||
0 /* ClusterHome */, -1 /* ClusterNone */}},
|
||||
.initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1,
|
||||
-1, -1 /* Insets */, 0 /* ClusterHome */,
|
||||
-1 /* ClusterNone */}},
|
||||
},
|
||||
{
|
||||
.config =
|
||||
|
||||
@@ -1 +1 @@
|
||||
945de3635b7f5a09244820eef56035c92fdbd324
|
||||
3b10f5094c5af9fe551093597fab007d1e148256
|
||||
|
||||
@@ -37,7 +37,7 @@ interface ISession {
|
||||
void generateChallenge();
|
||||
void revokeChallenge(in long challenge);
|
||||
android.hardware.biometrics.face.EnrollmentStageConfig[] getEnrollmentConfig(in android.hardware.biometrics.face.EnrollmentType enrollmentType);
|
||||
android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in android.hardware.common.NativeHandle previewSurface);
|
||||
android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in @nullable android.hardware.common.NativeHandle previewSurface);
|
||||
android.hardware.biometrics.common.ICancellationSignal authenticate(in long operationId);
|
||||
android.hardware.biometrics.common.ICancellationSignal detectInteraction();
|
||||
void enumerateEnrollments();
|
||||
|
||||
@@ -37,7 +37,7 @@ interface ISession {
|
||||
void generateChallenge();
|
||||
void revokeChallenge(in long challenge);
|
||||
android.hardware.biometrics.face.EnrollmentStageConfig[] getEnrollmentConfig(in android.hardware.biometrics.face.EnrollmentType enrollmentType);
|
||||
android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in android.hardware.common.NativeHandle previewSurface);
|
||||
android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in @nullable android.hardware.common.NativeHandle previewSurface);
|
||||
android.hardware.biometrics.common.ICancellationSignal authenticate(in long operationId);
|
||||
android.hardware.biometrics.common.ICancellationSignal detectInteraction();
|
||||
void enumerateEnrollments();
|
||||
|
||||
@@ -19,6 +19,15 @@ package android.hardware.biometrics.face;
|
||||
@VintfStability
|
||||
@Backing(type="byte")
|
||||
enum EnrollmentType {
|
||||
/**
|
||||
* Default enrollment type.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* Enrollment type for people with limited vision or mobility. For example,
|
||||
* enrollment of this type will not ask the user to move their head or
|
||||
* look directly at the device.
|
||||
*/
|
||||
ACCESSIBILITY,
|
||||
}
|
||||
|
||||
@@ -16,4 +16,23 @@
|
||||
|
||||
package android.hardware.biometrics.face;
|
||||
|
||||
@VintfStability @Backing(type="byte") enum FaceSensorType { UNKNOWN, RGB, IR }
|
||||
@VintfStability
|
||||
@Backing(type="byte")
|
||||
enum FaceSensorType {
|
||||
/**
|
||||
* Placeholder value used for default initialization of FaceSensorType.
|
||||
* This value means FaceSensorType wasn't explicitly initialized and must
|
||||
* be discarded by the recipient.
|
||||
*/
|
||||
UNKNOWN,
|
||||
|
||||
/**
|
||||
* The face sensor is an RGB camera.
|
||||
*/
|
||||
RGB,
|
||||
|
||||
/**
|
||||
* The face sensor is an infrared camera.
|
||||
*/
|
||||
IR,
|
||||
}
|
||||
|
||||
@@ -25,28 +25,30 @@ interface IFace {
|
||||
/**
|
||||
* getSensorProps:
|
||||
*
|
||||
* @return A list of properties for all face sensors available to the HAL.
|
||||
* @return A list of properties for all of the face sensors supported by the HAL.
|
||||
*/
|
||||
SensorProps[] getSensorProps();
|
||||
|
||||
/**
|
||||
* createSession:
|
||||
*
|
||||
* Creates a session that can be used by the framework to perform operations such as
|
||||
* enroll, authenticate, etc. for the given sensorId and userId.
|
||||
* Creates an instance of ISession that can be used by the framework to perform operations such
|
||||
* as ISession#enroll, ISession#authenticate, etc. for the given sensorId and userId.
|
||||
*
|
||||
* Calling this method while there is an active session is considered an error. If the
|
||||
* framework is in a bad state and for some reason cannot close its session, it should use
|
||||
* the reset method below.
|
||||
* Calling this method while there is an active session is considered an error. If the framework
|
||||
* wants to create a new session when it already has an active session, it must first cancel the
|
||||
* current operation if it's cancellable or wait until it completes. Then, the framework must
|
||||
* explicitly close the session with ISession#close. Once the framework receives
|
||||
* ISessionCallback#onSessionClosed, a new session can be created.
|
||||
*
|
||||
* Implementations must store user-specific state or metadata in /data/vendor_de/<user>/facedata
|
||||
* as specified by the SELinux policy. The directory /data/vendor_de is managed by vold (see
|
||||
* vold_prepare_subdirs.cpp). Implementations may store additional user-specific data, such as
|
||||
* embeddings or templates in StrongBox.
|
||||
* embeddings or templates, in StrongBox.
|
||||
*
|
||||
* @param sensorId The sensorId with which this session is being created.
|
||||
* @param userId The userId with which this session is being created.
|
||||
* @param cb A callback to notify the framework about the session's results and events.
|
||||
* @param sensorId The sensorId for which this session is being created.
|
||||
* @param userId The userId for which this session is being created.
|
||||
* @param cb A callback to notify the framework about the session's events.
|
||||
* @return A new session.
|
||||
*/
|
||||
ISession createSession(in int sensorId, in int userId, in ISessionCallback cb);
|
||||
|
||||
@@ -24,13 +24,12 @@ import android.hardware.common.NativeHandle;
|
||||
import android.hardware.keymaster.HardwareAuthToken;
|
||||
|
||||
/**
|
||||
* Operations that can be performed for unique sessions retrieved via IFace#createSession.
|
||||
* Operations defined within this interface can be divided into the following categories:
|
||||
* 1) Cancellable operations. These are usually the operations that can execute for several
|
||||
* minutes. To allow for cancellation, they return an instance of ICancellationSignal that
|
||||
* lets the framework cancel them by calling ICancellationSignal#cancel. If such an operation
|
||||
* is cancelled, it must notify the framework by calling ISessionCallback#onError with
|
||||
* Error::CANCELED.
|
||||
* minutes. To allow for cancellation, they return an instance of ICancellationSignal that
|
||||
* lets the framework cancel them by calling ICancellationSignal#cancel. If such an operation
|
||||
* is cancelled, it must notify the framework by calling ISessionCallback#onError with
|
||||
* Error::CANCELED.
|
||||
* 2) Non-cancellable operations. Such operations cannot be cancelled once started.
|
||||
*
|
||||
* The lifecycle of an operation ends when one of its terminal callbacks is called. For example,
|
||||
@@ -83,15 +82,20 @@ interface ISession {
|
||||
* | 0 | 10 | <Time4> | <Random4> |
|
||||
* ----------------------------------------------
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onChallengeGenerated
|
||||
*
|
||||
*/
|
||||
void generateChallenge();
|
||||
|
||||
/**
|
||||
* revokeChallenge:
|
||||
*
|
||||
* Revokes a challenge that was previously generated. Note that if an invalid combination of
|
||||
* parameters is requested, the implementation must still notify the framework using the
|
||||
* provided callback.
|
||||
* Revokes a challenge that was previously generated. Note that if a non-existent challenge is
|
||||
* provided, the HAL must still notify the framework using ISessionCallback#onChallengeRevoked.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onChallengeRevoked
|
||||
*
|
||||
* @param challenge Challenge that should be revoked.
|
||||
*/
|
||||
@@ -100,9 +104,9 @@ interface ISession {
|
||||
/**
|
||||
* getEnrollmentConfig:
|
||||
*
|
||||
* Returns the enrollment configuration depending on the provided enrollment type. Enrollment
|
||||
* configuration determines how many stages the enrollment will have and the requirements for
|
||||
* each of the stages.
|
||||
* Returns the enrollment configuration for the provided enrollment type. Enrollment
|
||||
* configuration determines how many stages the enrollment will have and the requirements
|
||||
* for each of the stages.
|
||||
*
|
||||
* @param enrollmentType See the EnrollmentType enum.
|
||||
* @return An EnrollmentStageConfig array that describes each enrollment stage.
|
||||
@@ -117,22 +121,28 @@ interface ISession {
|
||||
* At any point during enrollment, if a non-recoverable error occurs, the HAL must notify the
|
||||
* framework via ISessionCallback#onError with the applicable enrollment-specific error.
|
||||
*
|
||||
* Before capturing face data, the implementation must first verify the authenticity and
|
||||
* integrity of the provided HardwareAuthToken. In addition, it must check that the challenge
|
||||
* within the provided HardwareAuthToken is valid. See ISession#generateChallenge. If any of
|
||||
* the above checks fail, the framework must be notified using ISessionCallback#onError with
|
||||
* Error::UNABLE_TO_PROCESS.
|
||||
* Before capturing face data, the HAL must first verify the authenticity and integrity of the
|
||||
* provided HardwareAuthToken. In addition, it must check that the challenge within the provided
|
||||
* HardwareAuthToken is valid. See ISession#generateChallenge. If any of the above checks fail,
|
||||
* the framework must be notified using ISessionCallback#onError with Error::UNABLE_TO_PROCESS.
|
||||
*
|
||||
* During enrollment, the implementation may notify the framework via
|
||||
* ISessionCallback#onAcquired with messages that may be used to guide the user. This callback
|
||||
* can be invoked multiple times if necessary. Similarly, the framework may be notified of
|
||||
* enrollment progress changes via ISessionCallback#onEnrollmentProgress. Once the framework is
|
||||
* notified that there are 0 "remaining" steps, the framework may cache the "enrollmentId". See
|
||||
* During enrollment, the HAL may notify the framework via ISessionCallback#onAcquired with
|
||||
* messages that may be used to guide the user. This callback can be invoked multiple times if
|
||||
* necessary. Similarly, the framework may be notified of enrollment progress changes via
|
||||
* ISessionCallback#onEnrollmentProgress. Once the framework is notified that there are 0
|
||||
* "remaining" steps, the framework may cache the "enrollmentId". See
|
||||
* ISessionCallback#onEnrollmentProgress for more info.
|
||||
*
|
||||
* When a face is successfully added and before the framework is notified of remaining=0, the
|
||||
* implementation MUST update and associate this (sensorId, userId) pair with a new
|
||||
* entropy-encoded random identifier. See ISession#getAuthenticatorId for more information.
|
||||
* HAL must update and associate this (sensorId, userId) pair with a new entropy-encoded random
|
||||
* identifier. See ISession#getAuthenticatorId for more information.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onEnrollmentProgress(enrollmentId, remaining=0)
|
||||
*
|
||||
* Other applicable callbacks:
|
||||
* - ISessionCallback#onAcquired
|
||||
*
|
||||
* @param hat See above documentation.
|
||||
* @param enrollmentType See the EnrollmentType enum.
|
||||
@@ -144,7 +154,7 @@ interface ISession {
|
||||
* operation.
|
||||
*/
|
||||
ICancellationSignal enroll(in HardwareAuthToken hat, in EnrollmentType type,
|
||||
in Feature[] features, in NativeHandle previewSurface);
|
||||
in Feature[] features, in @nullable NativeHandle previewSurface);
|
||||
|
||||
/**
|
||||
* authenticate:
|
||||
@@ -154,15 +164,18 @@ interface ISession {
|
||||
* At any point during authentication, if a non-recoverable error occurs, the HAL must notify
|
||||
* the framework via ISessionCallback#onError with the applicable authentication-specific error.
|
||||
*
|
||||
* During authentication, the implementation may notify the framework via
|
||||
* ISessionCallback#onAcquired with messages that may be used to guide the user. This callback
|
||||
* can be invoked multiple times if necessary.
|
||||
* During authentication, the HAL may notify the framework via ISessionCallback#onAcquired with
|
||||
* messages that may be used to guide the user. This callback can be invoked multiple times if
|
||||
* necessary.
|
||||
*
|
||||
* The HAL must notify the framework of accepts/rejects via ISessionCallback#onAuthentication*.
|
||||
* The HAL must notify the framework of accepts/rejects via
|
||||
* ISessionCallback#onAuthenticationSucceeded and ISessionCallback#onAuthenticationFailed,
|
||||
* correspondingly.
|
||||
*
|
||||
* The authentication lifecycle ends when either
|
||||
* 1) A face is accepted, and ISessionCallback#onAuthenticationSucceeded is invoked, or
|
||||
* 2) Any non-recoverable error occurs (such as lockout). See the full list of
|
||||
* The authentication lifecycle ends when any of the following happens:
|
||||
* 1) A face is accepted, and ISessionCallback#onAuthenticationSucceeded is invoked.
|
||||
* 2) A face is rejected, and ISessionCallback#onAuthenticationFailed is invoked.
|
||||
* 3) Any non-recoverable error occurs (such as lockout). See the full list of
|
||||
* authentication-specific errors in the Error enum.
|
||||
*
|
||||
* Note that upon successful authentication, the lockout counter for this (sensorId, userId)
|
||||
@@ -174,16 +187,26 @@ interface ISession {
|
||||
* must be set with the operationId passed in during #authenticate. If the sensor is NOT
|
||||
* SensorStrength::STRONG, the HardwareAuthToken MUST be null.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onAuthenticationSucceeded
|
||||
* - ISessionCallback#onAuthenticationFailed
|
||||
*
|
||||
* Other applicable callbacks:
|
||||
* - ISessionCallback#onAcquired
|
||||
* - ISessionCallback#onLockoutTimed
|
||||
* - ISessionCallback#onLockoutPermanent
|
||||
*
|
||||
* @param operationId For sensors configured as SensorStrength::STRONG, this must be used ONLY
|
||||
* upon successful authentication and wrapped in the HardwareAuthToken's
|
||||
* "challenge" field and sent to the framework via
|
||||
* ISessionCallback#onAuthenticated. The operationId is an opaque identifier
|
||||
* created from a separate secure subsystem such as, but not limited to
|
||||
* KeyStore/KeyMaster. The HardwareAuthToken can then be used as an
|
||||
* attestation for the provided operation. For example, this is used
|
||||
* to unlock biometric-bound auth-per-use keys (see
|
||||
* ISessionCallback#onAuthenticationSucceeded. The operationId is an opaque
|
||||
* identifier created from a separate secure subsystem such as, but not
|
||||
* limited to KeyStore/KeyMaster. The HardwareAuthToken can then be used as
|
||||
* an attestation for the provided operation. For example, this is used to
|
||||
* unlock biometric-bound auth-per-use keys (see
|
||||
* setUserAuthenticationParameters in KeyGenParameterSpec.Builder and
|
||||
* KeyProtection.Builder.
|
||||
* KeyProtection.Builder).
|
||||
* @return ICancellationSignal An object that can be used by the framework to cancel this
|
||||
* operation.
|
||||
*/
|
||||
@@ -193,32 +216,36 @@ interface ISession {
|
||||
* detectInteraction:
|
||||
*
|
||||
* A request to start looking for faces without performing matching. Must only be called if
|
||||
* SensorProps#supportsDetectInteraction is true. If invoked on implementations that do not
|
||||
* support this functionality, the HAL must respond with ISession#onError(UNABLE_TO_PROCESS, 0).
|
||||
* SensorProps#supportsDetectInteraction is true. If invoked on HALs that do not support this
|
||||
* functionality, the HAL must respond with ISession#onError(UNABLE_TO_PROCESS, 0).
|
||||
*
|
||||
* The framework will use this method in cases where determing user presence is required, but
|
||||
* identifying/authentication is not. For example, when the device is encrypted (first boot) or
|
||||
* in lockdown mode.
|
||||
* The framework will use this operation in cases where determining user presence is required,
|
||||
* but identifying/authenticating is not. For example, when the device is encrypted (first boot)
|
||||
* or in lockdown mode.
|
||||
*
|
||||
* At any point during detectInteraction, if a non-recoverable error occurs, the HAL must notify
|
||||
* the framework via ISessionCallback#onError with the applicable error.
|
||||
*
|
||||
* The implementation must only check for a face-like image was detected (e.g. to
|
||||
* minimize interactions due to non-face objects), and the lockout counter must not
|
||||
* be modified.
|
||||
* The HAL must only check whether a face-like image was detected (e.g. to minimize interactions
|
||||
* due to non-face objects), and the lockout counter must not be modified.
|
||||
*
|
||||
* Upon detecting any face, the implementation must invoke
|
||||
* ISessionCallback#onInteractionDetected.
|
||||
* Upon detecting any face, the HAL must invoke ISessionCallback#onInteractionDetected.
|
||||
*
|
||||
* The lifecycle of this operation ends when either
|
||||
* The lifecycle of this operation ends when either:
|
||||
* 1) Any face is detected and the framework is notified via
|
||||
* ISessionCallback#onInteractiondetected
|
||||
* 2) The operation was cancelled by the framework (see ICancellationSignal)
|
||||
* 3) An error occurred, for example ERROR::TIMEOUT
|
||||
* ISessionCallback#onInteractionDetected.
|
||||
* 2) An error occurrs, for example Error::TIMEOUT.
|
||||
*
|
||||
* Note that if the operation is canceled, the implementation must notify the framework via
|
||||
* Note that if the operation is canceled, the HAL must notify the framework via
|
||||
* ISessionCallback#onError with Error::CANCELED.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onInteractionDetected
|
||||
*
|
||||
* Other applicable callbacks:
|
||||
* - ISessionCallback#onAcquired
|
||||
*
|
||||
* @return ICancellationSignal An object that can be used by the framework to cancel this
|
||||
* operation.
|
||||
*/
|
||||
@@ -227,12 +254,14 @@ interface ISession {
|
||||
/*
|
||||
* enumerateEnrollments:
|
||||
*
|
||||
* A request to enumerate (list) the enrollments for this (sensorId, userId) pair. The
|
||||
* framework typically uses this to ensure that its cache is in sync with the HAL.
|
||||
* A request to enumerate (list) the enrollments for this (sensorId, userId) pair. The framework
|
||||
* typically uses this to ensure that its cache is in sync with the HAL.
|
||||
*
|
||||
* The implementation must then notify the framework with a list of enrollments applicable
|
||||
* for the current session via ISessionCallback#onEnrollmentsEnumerated.
|
||||
* The HAL must then notify the framework with a list of enrollments applicable for the current
|
||||
* session via ISessionCallback#onEnrollmentsEnumerated.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onEnrollmentsEnumerated
|
||||
*/
|
||||
void enumerateEnrollments();
|
||||
|
||||
@@ -242,8 +271,12 @@ interface ISession {
|
||||
* A request to remove the enrollments for this (sensorId, userId) pair.
|
||||
*
|
||||
* After removing the enrollmentIds from everywhere necessary (filesystem, secure subsystems,
|
||||
* etc), the implementation must notify the framework via ISessionCallback#onEnrollmentsRemoved.
|
||||
* etc), the HAL must notify the framework via ISessionCallback#onEnrollmentsRemoved.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onEnrollmentsRemoved
|
||||
*
|
||||
* @param enrollmentIds a list of enrollments that should be removed.
|
||||
*/
|
||||
void removeEnrollments(in int[] enrollmentIds);
|
||||
|
||||
@@ -257,6 +290,10 @@ interface ISession {
|
||||
*
|
||||
* The HAL must notify the framework about the result by calling
|
||||
* ISessionCallback#onFeaturesRetrieved.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onFeaturesRetrieved
|
||||
*/
|
||||
void getFeatures();
|
||||
|
||||
@@ -264,15 +301,19 @@ interface ISession {
|
||||
* setFeature:
|
||||
*
|
||||
* Enables or disables a feature for this (sensorId, userId) pair. Because certain features may
|
||||
* decrease security, the user must enter their password before this method is invoked
|
||||
* (see @param hat). The HAL must verify the hat before changing any feature state.
|
||||
* decrease security, the user must enter their password before this operation is invoked
|
||||
* (see @param hat). The HAL must verify the HAT before changing any feature state.
|
||||
*
|
||||
* If the hat is invalid or if the user is not enrolled, the HAL must invoke
|
||||
* If the HAT is invalid or if the user is not enrolled, the HAL must invoke
|
||||
* ISessionCallback#onError with Error::UNABLE_TO_PROCESS.
|
||||
*
|
||||
* After the feature is successfully set, the HAL must notify the framework by calling
|
||||
* ISessionCallback#onFeatureSet.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onFeatureSet
|
||||
*
|
||||
* @param hat HardwareAuthToken See above documentation.
|
||||
* @param feature The feature to be enabled or disabled.
|
||||
* @param enabled Whether the provided features should be enabled or disabled.
|
||||
@@ -295,8 +336,8 @@ interface ISession {
|
||||
* KeyProtection.Builder.setInvalidatedByBiometricEnrollment.
|
||||
*
|
||||
* In addition, upon successful face authentication, the signed HAT that is returned to
|
||||
* the framework via ISessionCallback#onAuthenticated must contain this identifier in the
|
||||
* authenticatorId field.
|
||||
* the framework via ISessionCallback#onAuthenticationSucceeded must contain this identifier in
|
||||
* the authenticatorId field.
|
||||
*
|
||||
* Returns an entropy-encoded random identifier associated with the current set of enrollments
|
||||
* via ISessionCallback#onAuthenticatorIdRetrieved. The authenticatorId
|
||||
@@ -305,20 +346,21 @@ interface ISession {
|
||||
* 3) MUST not change if a face is deleted.
|
||||
* 4) MUST be an entropy-encoded random number
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onAuthenticatorIdRetrieved
|
||||
*/
|
||||
void getAuthenticatorId();
|
||||
|
||||
/**
|
||||
* invalidateAuthenticatorId:
|
||||
*
|
||||
* This method only applies to sensors that are configured as SensorStrength::STRONG. If invoked
|
||||
* by the framework for sensor of other strengths, the HAL should immediately invoke
|
||||
* This operation only applies to sensors that are configured as SensorStrength::STRONG. If
|
||||
* invoked by the framework for sensors of other strengths, the HAL should immediately invoke
|
||||
* ISessionCallback#onAuthenticatorIdInvalidated.
|
||||
*
|
||||
* The following only applies to sensors that are configured as SensorStrength::STRONG.
|
||||
*
|
||||
* When invoked by the framework, the implementation must perform the following sequence of
|
||||
* events:
|
||||
* When invoked by the framework, the HAL must perform the following sequence of events:
|
||||
* 1) Update the authenticatorId with a new entropy-encoded random number
|
||||
* 2) Persist the new authenticatorId to non-ephemeral storage
|
||||
* 3) Notify the framework that the above is completed, via
|
||||
@@ -326,18 +368,20 @@ interface ISession {
|
||||
*
|
||||
* A practical use case of invalidation would be when the user adds a new enrollment to a sensor
|
||||
* managed by a different HAL instance. The public android.security.keystore APIs bind keys to
|
||||
* "all biometrics" rather than "face-only" or "face-only" (see #getAuthenticatorId
|
||||
* for more details). As such, the framework would coordinate invalidation across multiple
|
||||
* biometric HALs as necessary.
|
||||
* "all biometrics" rather than "fingerprint-only" or "face-only" (see #getAuthenticatorId for
|
||||
* more details). As such, the framework would coordinate invalidation across multiple biometric
|
||||
* HALs as necessary.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onAuthenticatorIdInvalidated
|
||||
*/
|
||||
void invalidateAuthenticatorId();
|
||||
|
||||
/**
|
||||
* resetLockout:
|
||||
*
|
||||
* Requests the implementation to clear the lockout counter. Upon receiving this request, the
|
||||
* implementation must perform the following:
|
||||
* Requests the HAL to clear the lockout counter. Upon receiving this request, the HAL must
|
||||
* perform the following:
|
||||
* 1) Verify the authenticity and integrity of the provided HAT
|
||||
* 2) Verify that the timestamp provided within the HAT is relatively recent (e.g. on the
|
||||
* order of minutes, not hours).
|
||||
@@ -373,6 +417,9 @@ interface ISession {
|
||||
* See the Android CDD section 7.3.10 for the full set of lockout and rate-limiting
|
||||
* requirements.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onLockoutCleared
|
||||
*
|
||||
* @param hat HardwareAuthToken See above documentation.
|
||||
*/
|
||||
void resetLockout(in HardwareAuthToken hat);
|
||||
@@ -384,9 +431,14 @@ interface ISession {
|
||||
* If the HAL is busy performing a cancellable operation, the operation must be explicitly
|
||||
* cancelled with a call to ICancellationSignal#cancel before the session can be closed.
|
||||
*
|
||||
* After a session is closed, the HAL must notify the framework by calling
|
||||
* ISessionCallback#onSessionClosed.
|
||||
*
|
||||
* All sessions must be explicitly closed. Calling IFace#createSession while there is an active
|
||||
* session is considered an error.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onSessionClosed
|
||||
*/
|
||||
void close();
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ interface ISessionCallback {
|
||||
|
||||
/**
|
||||
* This method must only be used to notify the framework during the following operations:
|
||||
* 1) ISession#authenticate
|
||||
* 2) ISession#detectInteraction
|
||||
* - ISession#authenticate
|
||||
* - ISession#detectInteraction
|
||||
*
|
||||
* These messages may be used to provide user guidance multiple times if necessary per
|
||||
* operation.
|
||||
* These messages may be used to provide user guidance multiple times per operation if
|
||||
* necessary.
|
||||
*
|
||||
* @param frame See the AuthenticationFrame enum.
|
||||
*/
|
||||
@@ -51,8 +51,8 @@ interface ISessionCallback {
|
||||
* This method must only be used to notify the framework during the ISession#enroll
|
||||
* operation.
|
||||
*
|
||||
* These messages may be used to provide user guidance multiple times if necessary per
|
||||
* operation.
|
||||
* These messages may be used to provide user guidance multiple times per operation if
|
||||
* necessary.
|
||||
*
|
||||
* @param frame See the EnrollmentFrame enum.
|
||||
*/
|
||||
@@ -60,18 +60,18 @@ interface ISessionCallback {
|
||||
|
||||
/**
|
||||
* This method must only be used to notify the framework during the following operations:
|
||||
* 1) ISession#enroll
|
||||
* 2) ISession#authenticate
|
||||
* 3) ISession#detectInteraction
|
||||
* 4) ISession#invalidateAuthenticatorId
|
||||
* 5) ISession#resetLockout
|
||||
* - ISession#enroll
|
||||
* - ISession#authenticate
|
||||
* - ISession#detectInteraction
|
||||
* - ISession#invalidateAuthenticatorId
|
||||
* - ISession#resetLockout
|
||||
*
|
||||
* These messages may be used to notify the framework or user that a non-recoverable error
|
||||
* has occurred. The operation is finished, and the HAL must proceed with the next operation
|
||||
* or return to the idling state.
|
||||
*
|
||||
* Note that cancellation (see common::ICancellationSignal) and preemption must be followed with
|
||||
* an Error::CANCELED message.
|
||||
* Note that cancellation (see common::ICancellationSignal) must be followed with an
|
||||
* Error::CANCELED message.
|
||||
*
|
||||
* @param error See the Error enum.
|
||||
* @param vendorCode Only valid if error == Error::VENDOR. The vendorCode must be used to index
|
||||
|
||||
@@ -34,12 +34,15 @@ class CancellationSignal : public common::BnCancellationSignal {
|
||||
}
|
||||
};
|
||||
|
||||
Session::Session(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
|
||||
Session::Session(std::shared_ptr<ISessionCallback> cb)
|
||||
: cb_(std::move(cb)), mRandom(std::mt19937::default_seed) {}
|
||||
|
||||
ndk::ScopedAStatus Session::generateChallenge() {
|
||||
LOG(INFO) << "generateChallenge";
|
||||
if (cb_) {
|
||||
cb_->onChallengeGenerated(0);
|
||||
std::uniform_int_distribution<int64_t> dist;
|
||||
auto challenge = dist(mRandom);
|
||||
cb_->onChallengeGenerated(challenge);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
@@ -60,9 +63,13 @@ ndk::ScopedAStatus Session::getEnrollmentConfig(EnrollmentType /*enrollmentType*
|
||||
|
||||
ndk::ScopedAStatus Session::enroll(
|
||||
const keymaster::HardwareAuthToken& /*hat*/, EnrollmentType /*enrollmentType*/,
|
||||
const std::vector<Feature>& /*features*/, const NativeHandle& /*previewSurface*/,
|
||||
const std::vector<Feature>& /*features*/,
|
||||
const std::optional<NativeHandle>& /*previewSurface*/,
|
||||
std::shared_ptr<biometrics::common::ICancellationSignal>* /*return_val*/) {
|
||||
LOG(INFO) << "enroll";
|
||||
if (cb_) {
|
||||
cb_->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -100,6 +107,10 @@ ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& /*enro
|
||||
|
||||
ndk::ScopedAStatus Session::getFeatures() {
|
||||
LOG(INFO) << "getFeatures";
|
||||
if (cb_) {
|
||||
// Must error out with UNABLE_TO_PROCESS when no faces are enrolled.
|
||||
cb_->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -119,6 +130,9 @@ ndk::ScopedAStatus Session::getAuthenticatorId() {
|
||||
|
||||
ndk::ScopedAStatus Session::invalidateAuthenticatorId() {
|
||||
LOG(INFO) << "invalidateAuthenticatorId";
|
||||
if (cb_) {
|
||||
cb_->onAuthenticatorIdInvalidated(0);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <aidl/android/hardware/biometrics/face/BnSession.h>
|
||||
#include <aidl/android/hardware/biometrics/face/ISessionCallback.h>
|
||||
|
||||
@@ -39,7 +41,7 @@ class Session : public BnSession {
|
||||
|
||||
ndk::ScopedAStatus enroll(const keymaster::HardwareAuthToken& hat,
|
||||
EnrollmentType enrollmentType, const std::vector<Feature>& features,
|
||||
const NativeHandle& previewSurface,
|
||||
const std::optional<NativeHandle>& previewSurface,
|
||||
std::shared_ptr<common::ICancellationSignal>* return_val) override;
|
||||
|
||||
ndk::ScopedAStatus authenticate(
|
||||
@@ -68,6 +70,7 @@ class Session : public BnSession {
|
||||
|
||||
private:
|
||||
std::shared_ptr<ISessionCallback> cb_;
|
||||
std::mt19937 mRandom;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::biometrics::face
|
||||
|
||||
@@ -29,16 +29,26 @@ namespace {
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
using aidl::android::hardware::common::NativeHandle;
|
||||
|
||||
constexpr int kSensorId = 0;
|
||||
constexpr int kUserId = 0;
|
||||
|
||||
class SessionCallback : public BnSessionCallback {
|
||||
public:
|
||||
ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
|
||||
ndk::ScopedAStatus onChallengeGenerated(int64_t challenge) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnChallengeGeneratedInvoked = true;
|
||||
mGeneratedChallenge = challenge;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onChallengeRevoked(int64_t /*challenge*/) override {
|
||||
ndk::ScopedAStatus onChallengeRevoked(int64_t challenge) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnChallengeRevokedInvoked = true;
|
||||
mRevokedChallenge = challenge;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -83,15 +93,24 @@ class SessionCallback : public BnSessionCallback {
|
||||
|
||||
ndk::ScopedAStatus onEnrollmentsEnumerated(
|
||||
const std::vector<int32_t>& /*enrollmentIds*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnEnrollmentsEnumeratedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onEnrollmentsRemoved(
|
||||
const std::vector<int32_t>& /*enrollmentIds*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnEnrollmentsRemovedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onFeaturesRetrieved(const std::vector<Feature>& /*features*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnFeaturesRetrievedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -100,10 +119,16 @@ class SessionCallback : public BnSessionCallback {
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t /*authenticatorId*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnAuthenticatorIdRetrievedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t /*newAuthenticatorId*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnAuthenticatorIdInvalidatedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -118,45 +143,181 @@ class SessionCallback : public BnSessionCallback {
|
||||
std::condition_variable mCv;
|
||||
Error mError = Error::UNKNOWN;
|
||||
int32_t mVendorCode = 0;
|
||||
int64_t mGeneratedChallenge = 0;
|
||||
int64_t mRevokedChallenge = 0;
|
||||
bool mOnChallengeGeneratedInvoked = false;
|
||||
bool mOnChallengeRevokedInvoked = false;
|
||||
bool mOnErrorInvoked = false;
|
||||
bool mOnEnrollmentsEnumeratedInvoked = false;
|
||||
bool mOnEnrollmentsRemovedInvoked = false;
|
||||
bool mOnFeaturesRetrievedInvoked = false;
|
||||
bool mOnAuthenticatorIdRetrievedInvoked = false;
|
||||
bool mOnAuthenticatorIdInvalidatedInvoked = false;
|
||||
bool mOnSessionClosedInvoked = false;
|
||||
};
|
||||
|
||||
class Face : public testing::TestWithParam<std::string> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
|
||||
ASSERT_NE(binder, nullptr);
|
||||
mHal = IFace::fromBinder(ndk::SpAIBinder(binder));
|
||||
// Prepare the callback.
|
||||
mCb = ndk::SharedRefBase::make<SessionCallback>();
|
||||
|
||||
int retries = 0;
|
||||
bool isOk = false;
|
||||
// If the first attempt to create a session fails, we try to create a session again. The
|
||||
// first attempt might fail if the framework already has an active session. The AIDL
|
||||
// contract doesn't allow to create a new session without closing the old one. However, we
|
||||
// can't close the framework's session from VTS. The expectation here is that the HAL will
|
||||
// crash after the first illegal attempt to create a session, then it will restart, and then
|
||||
// we'll be able to create a session.
|
||||
do {
|
||||
// Get an instance of the HAL.
|
||||
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
|
||||
ASSERT_NE(binder, nullptr);
|
||||
mHal = IFace::fromBinder(ndk::SpAIBinder(binder));
|
||||
|
||||
// Create a session.
|
||||
isOk = mHal->createSession(kSensorId, kUserId, mCb, &mSession).isOk();
|
||||
++retries;
|
||||
} while (!isOk && retries < 2);
|
||||
|
||||
ASSERT_TRUE(isOk);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Close the mSession.
|
||||
ASSERT_TRUE(mSession->close().isOk());
|
||||
|
||||
// Make sure the mSession is closed.
|
||||
auto lock = std::unique_lock<std::mutex>(mCb->mMutex);
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnSessionClosedInvoked; });
|
||||
}
|
||||
|
||||
std::shared_ptr<IFace> mHal;
|
||||
std::shared_ptr<SessionCallback> mCb;
|
||||
std::shared_ptr<ISession> mSession;
|
||||
};
|
||||
|
||||
TEST_P(Face, AuthenticateTest) {
|
||||
// Prepare the callback.
|
||||
auto cb = ndk::SharedRefBase::make<SessionCallback>();
|
||||
TEST_P(Face, GetSensorPropsWorksTest) {
|
||||
std::vector<SensorProps> sensorProps;
|
||||
|
||||
// Create a session
|
||||
std::shared_ptr<ISession> session;
|
||||
ASSERT_TRUE(mHal->createSession(kSensorId, kUserId, cb, &session).isOk());
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mHal->getSensorProps(&sensorProps).isOk());
|
||||
|
||||
// Call authenticate
|
||||
// Make sure the sensorProps aren't empty.
|
||||
ASSERT_FALSE(sensorProps.empty());
|
||||
ASSERT_FALSE(sensorProps[0].commonProps.componentInfo.empty());
|
||||
}
|
||||
|
||||
TEST_P(Face, EnrollWithBadHatResultsInErrorTest) {
|
||||
// Call the method.
|
||||
auto hat = keymaster::HardwareAuthToken{};
|
||||
std::shared_ptr<common::ICancellationSignal> cancellationSignal;
|
||||
ASSERT_TRUE(session->authenticate(0 /* operationId */, &cancellationSignal).isOk());
|
||||
ASSERT_TRUE(
|
||||
mSession->enroll(hat, EnrollmentType::DEFAULT, {}, std::nullopt, &cancellationSignal)
|
||||
.isOk());
|
||||
|
||||
auto lock = std::unique_lock<std::mutex>(cb->mMutex);
|
||||
cb->mCv.wait(lock, [&cb] { return cb->mOnErrorInvoked; });
|
||||
// Get the results
|
||||
EXPECT_EQ(cb->mError, Error::UNABLE_TO_PROCESS);
|
||||
EXPECT_EQ(cb->mVendorCode, 0);
|
||||
// Make sure an error is returned.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnErrorInvoked; });
|
||||
EXPECT_EQ(mCb->mError, Error::UNABLE_TO_PROCESS);
|
||||
EXPECT_EQ(mCb->mVendorCode, 0);
|
||||
}
|
||||
|
||||
TEST_P(Face, GenerateChallengeProducesUniqueChallengesTest) {
|
||||
static constexpr int kIterations = 100;
|
||||
|
||||
auto challenges = std::set<int>{};
|
||||
for (unsigned int i = 0; i < kIterations; ++i) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->generateChallenge().isOk());
|
||||
|
||||
// Check that the generated challenge is unique and not 0.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeGeneratedInvoked; });
|
||||
ASSERT_NE(mCb->mGeneratedChallenge, 0);
|
||||
ASSERT_EQ(challenges.find(mCb->mGeneratedChallenge), challenges.end());
|
||||
|
||||
challenges.insert(mCb->mGeneratedChallenge);
|
||||
mCb->mOnChallengeGeneratedInvoked = false;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Face, RevokeChallengeWorksForNonexistentChallengeTest) {
|
||||
const int64_t nonexistentChallenge = 123;
|
||||
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->revokeChallenge(nonexistentChallenge).isOk());
|
||||
|
||||
// Check that the challenge is revoked and matches the requested challenge.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeRevokedInvoked; });
|
||||
ASSERT_EQ(mCb->mRevokedChallenge, nonexistentChallenge);
|
||||
}
|
||||
|
||||
TEST_P(Face, RevokeChallengeWorksForExistentChallengeTest) {
|
||||
// Generate a challenge.
|
||||
ASSERT_TRUE(mSession->generateChallenge().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeGeneratedInvoked; });
|
||||
lock.unlock();
|
||||
|
||||
// Close the session
|
||||
ASSERT_TRUE(session->close().isOk());
|
||||
// Revoke the challenge.
|
||||
ASSERT_TRUE(mSession->revokeChallenge(mCb->mGeneratedChallenge).isOk());
|
||||
|
||||
// Check that the challenge is revoked and matches the requested challenge.
|
||||
lock.lock();
|
||||
cb->mCv.wait(lock, [&cb] { return cb->mOnSessionClosedInvoked; });
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeRevokedInvoked; });
|
||||
ASSERT_EQ(mCb->mRevokedChallenge, mCb->mGeneratedChallenge);
|
||||
}
|
||||
|
||||
TEST_P(Face, EnumerateEnrollmentsWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->enumerateEnrollments().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnEnrollmentsEnumeratedInvoked; });
|
||||
}
|
||||
|
||||
TEST_P(Face, RemoveEnrollmentsWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->removeEnrollments({}).isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnEnrollmentsRemovedInvoked; });
|
||||
}
|
||||
|
||||
TEST_P(Face, GetFeaturesWithoutEnrollmentsResultsInUnableToProcess) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->getFeatures().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnErrorInvoked; });
|
||||
EXPECT_EQ(mCb->mError, Error::UNABLE_TO_PROCESS);
|
||||
EXPECT_EQ(mCb->mVendorCode, 0);
|
||||
}
|
||||
|
||||
TEST_P(Face, GetAuthenticatorIdWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->getAuthenticatorId().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnAuthenticatorIdRetrievedInvoked; });
|
||||
}
|
||||
|
||||
TEST_P(Face, InvalidateAuthenticatorIdWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->invalidateAuthenticatorId().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnAuthenticatorIdInvalidatedInvoked; });
|
||||
}
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Face);
|
||||
|
||||
@@ -19,6 +19,10 @@ package android.hardware.biometrics.fingerprint@2.3;
|
||||
import @2.2::IBiometricsFingerprint;
|
||||
|
||||
/**
|
||||
* New use of this interface is strongly discouraged. The recommended option is
|
||||
* to use the AIDL interface, android.hardware.biometrics.fingerprint
|
||||
* (IFingerprint).
|
||||
*
|
||||
* The interface for biometric fingerprint authentication.
|
||||
*/
|
||||
interface IBiometricsFingerprint extends @2.2::IBiometricsFingerprint {
|
||||
|
||||
@@ -25,31 +25,31 @@ interface IFingerprint {
|
||||
/**
|
||||
* getSensorProps:
|
||||
*
|
||||
* @return A list of properties for all sensors that an instance of the HAL supports.
|
||||
* @return A list of properties for all of the fingerprint sensors supported by the HAL.
|
||||
*/
|
||||
SensorProps[] getSensorProps();
|
||||
|
||||
/**
|
||||
* createSession:
|
||||
*
|
||||
* Creates a instance of ISession which can be used by the framework to perform operations
|
||||
* such as ISession#enroll, ISession#authenticate, etc. for the given sensorId and userId.
|
||||
* Creates an instance of ISession that can be used by the framework to perform operations such
|
||||
* as ISession#enroll, ISession#authenticate, etc. for the given sensorId and userId.
|
||||
*
|
||||
* Calling this method while there is an active session is considered an error. If the framework
|
||||
* wants to create a new session when it already has an active session, it must first cancel the
|
||||
* current operation if it's cancellable, or wait until it completes. Then, the framework must
|
||||
* current operation if it's cancellable or wait until it completes. Then, the framework must
|
||||
* explicitly close the session with ISession#close. Once the framework receives
|
||||
* ISessionCallback#onSessionClosed, a new session can be created.
|
||||
*
|
||||
* Implementations must store user-specific state or metadata in /data/vendor_de/<user>/fpdata
|
||||
* as specified by the SeLinux policy. This directory is created/removed by vold (see
|
||||
* as specified by the SELinux policy. The directory /data/vendor_de is managed by vold (see
|
||||
* vold_prepare_subdirs.cpp). Implementations may store additional user-specific data, such as
|
||||
* embeddings or templates in StrongBox.
|
||||
* embeddings or templates, in StrongBox.
|
||||
*
|
||||
* @param sensorId The sensor with which this session is being created.
|
||||
* @param userId The userId with which this session is being created.
|
||||
* @param cb Used to notify the framework.
|
||||
* @return A new session
|
||||
* @param sensorId The sensorId for which this session is being created.
|
||||
* @param userId The userId for which this session is being created.
|
||||
* @param cb A callback to notify the framework about the session's events.
|
||||
* @return A new session.
|
||||
*/
|
||||
ISession createSession(in int sensorId, in int userId, in ISessionCallback cb);
|
||||
}
|
||||
|
||||
@@ -20,30 +20,29 @@ import android.hardware.biometrics.common.ICancellationSignal;
|
||||
import android.hardware.keymaster.HardwareAuthToken;
|
||||
|
||||
/**
|
||||
* Operations that can be performed for unique sessions retrieved via IFingerprint#createSession.
|
||||
* Methods defined within this interface can be split into the following categories:
|
||||
* 1) Non-interrupting operations. These operations are handled by the HAL in FIFO order.
|
||||
* 1a) Cancellable operations. These are usually the operations that can execute for several
|
||||
* minutes. To allow for cancellation, they return an instance of ICancellationSignal that
|
||||
* lets the framework cancel them by calling ICancellationSignal#cancel. If such an operation
|
||||
* is cancelled, it must notify the framework by calling ISessionCallback#onError with
|
||||
* Error::CANCELED.
|
||||
* 1b) Non-cancellable operations. Such operations cannot be cancelled once started.
|
||||
* Operations defined within this interface can be split into the following categories:
|
||||
* 1) Non-interrupting operations. These operations are handled by the HAL in a FIFO order.
|
||||
* 1a) Cancellable operations. These operations can usually run for several minutes. To allow
|
||||
* for cancellation, they return an instance of ICancellationSignal that allows the
|
||||
* framework to cancel them by calling ICancellationSignal#cancel. If such an operation is
|
||||
* cancelled, it must notify the framework by calling ISessionCallback#onError with
|
||||
* Error::CANCELED.
|
||||
* 1b) Non-cancellable operations. Such operations cannot be cancelled once started.
|
||||
* 2) Interrupting operations. These operations may be invoked by the framework immediately,
|
||||
* regardless of whether another operation is executing. For example, on devices with sensors
|
||||
* of FingerprintSensorType::UNDER_DISPLAY_*, ISession#onFingerDown may be invoked while the
|
||||
* of FingerprintSensorType::UNDER_DISPLAY_*, ISession#onPointerDown may be invoked while the
|
||||
* HAL is executing ISession#enroll, ISession#authenticate or ISession#detectInteraction.
|
||||
*
|
||||
* The lifecycle of a non-interrupting operation ends when one of its terminal callbacks is called.
|
||||
* For example, ISession#authenticate is considered completed when either of the following callbacks
|
||||
* is called: ISessionCallback#onError or ISessionCallback#onAuthenticationSucceeded.
|
||||
* The lifecycle of a non-interrupting operation ends when one of its final callbacks is called.
|
||||
* For example, ISession#authenticate is considered completed when either ISessionCallback#onError
|
||||
* or ISessionCallback#onAuthenticationSucceeded is called.
|
||||
*
|
||||
* The lifecycle of an interrupting operation ends when it returns. Interrupting operations do not
|
||||
* have callbacks.
|
||||
*
|
||||
* ISession only supports execution of one non-interrupting operation at a time, regardless of
|
||||
* whether it's cancellable. The framework must wait for a corresponding callback indicating the end
|
||||
* of the current non-interrupting operation before a new non-interrupting operation can be started.
|
||||
* whether it's cancellable. The framework must wait for a callback indicating the end of the
|
||||
* current non-interrupting operation before a new non-interrupting operation can be started.
|
||||
*/
|
||||
@VintfStability
|
||||
interface ISession {
|
||||
@@ -89,15 +88,19 @@ interface ISession {
|
||||
* | 0 | 10 | <Time4> | <Random4> |
|
||||
* ----------------------------------------------
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onChallengeGenerated
|
||||
*/
|
||||
void generateChallenge();
|
||||
|
||||
/**
|
||||
* revokeChallenge:
|
||||
*
|
||||
* Revokes a challenge that was previously generated. Note that if an invalid combination of
|
||||
* parameters is requested, the implementation must still notify the framework using the
|
||||
* provided callback.
|
||||
* Revokes a challenge that was previously generated. Note that if a non-existent challenge is
|
||||
* provided, the HAL must still notify the framework using ISessionCallback#onChallengeRevoked.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onChallengeRevoked
|
||||
*
|
||||
* @param challenge Challenge that should be revoked.
|
||||
*/
|
||||
@@ -111,26 +114,33 @@ interface ISession {
|
||||
* At any point during enrollment, if a non-recoverable error occurs, the HAL must notify the
|
||||
* framework via ISessionCallback#onError with the applicable enrollment-specific error.
|
||||
*
|
||||
* Before capturing fingerprint data, the implementation must first verify the authenticity and
|
||||
* integrity of the provided HardwareAuthToken. In addition, it must check that the challenge
|
||||
* within the provided HardwareAuthToken is valid. See ISession#generateChallenge. If any of
|
||||
* the above checks fail, the framework must be notified via ISessionCallback#onError and the
|
||||
* HAL must notify the framework when it returns to the idle state. See
|
||||
* Before capturing fingerprint data, the HAL must first verify the authenticity and integrity
|
||||
* of the provided HardwareAuthToken. In addition, it must check that the challenge within the
|
||||
* provided HardwareAuthToken is valid. See ISession#generateChallenge. If any of the above
|
||||
* checks fail, the framework must be notified using ISessionCallback#onError with
|
||||
* Error::UNABLE_TO_PROCESS.
|
||||
*
|
||||
* During enrollment, the implementation may notify the framework via
|
||||
* ISessionCallback#onAcquired with messages that may be used to guide the user. This callback
|
||||
* can be invoked multiple times if necessary. Similarly, the framework may be notified of
|
||||
* enrollment progress changes via ISessionCallback#onEnrollmentProgress. Once the framework is
|
||||
* notified that there are 0 "remaining" steps, the framework may cache the "enrollmentId". See
|
||||
* ISessionCallback#onEnrollmentProgress for more info. The HAL must notify the framework once
|
||||
* it returns to the idle state.
|
||||
* During enrollment, the HAL may notify the framework via ISessionCallback#onAcquired with
|
||||
* messages that may be used to guide the user. This callback can be invoked multiple times if
|
||||
* necessary. Similarly, the framework may be notified of enrollment progress changes via
|
||||
* ISessionCallback#onEnrollmentProgress. Once the framework is notified that there are 0
|
||||
* "remaining" steps, the framework may cache the "enrollmentId". See
|
||||
* ISessionCallback#onEnrollmentProgress for more info.
|
||||
*
|
||||
* When a finger is successfully added and before the framework is notified of remaining=0, the
|
||||
* implementation MUST update and associate this (sensorId, userId) pair with a new new
|
||||
* entropy-encoded random identifier. See ISession#getAuthenticatorId for more information.
|
||||
* HAL must update and associate this (sensorId, userId) pair with a new entropy-encoded random
|
||||
* identifier. See ISession#getAuthenticatorId for more information.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onEnrollmentProgress(enrollmentId, remaining=0)
|
||||
*
|
||||
* Other applicable callbacks:
|
||||
* - ISessionCallback#onAcquired
|
||||
*
|
||||
* @param hat See above documentation.
|
||||
* @return ICancellationSignal An object that can be used by the framework to cancel this
|
||||
* operation.
|
||||
*/
|
||||
ICancellationSignal enroll(in HardwareAuthToken hat);
|
||||
|
||||
@@ -142,14 +152,16 @@ interface ISession {
|
||||
* At any point during authentication, if a non-recoverable error occurs, the HAL must notify
|
||||
* the framework via ISessionCallback#onError with the applicable authentication-specific error.
|
||||
*
|
||||
* During authentication, the implementation may notify the framework via
|
||||
* ISessionCallback#onAcquired with messages that may be used to guide the user. This callback
|
||||
* can be invoked multiple times if necessary.
|
||||
* During authentication, the HAL may notify the framework via ISessionCallback#onAcquired with
|
||||
* messages that may be used to guide the user. This callback can be invoked multiple times if
|
||||
* necessary.
|
||||
*
|
||||
* The HAL must notify the framework of accepts/rejects via ISessionCallback#onAuthentication*.
|
||||
* The HAL must notify the framework of accepts and rejects via
|
||||
* ISessionCallback#onAuthenticationSucceeded and ISessionCallback#onAuthenticationFailed,
|
||||
* correspondingly.
|
||||
*
|
||||
* The authentication lifecycle ends when either
|
||||
* 1) A fingerprint is accepted, and ISessionCallback#onAuthenticationSucceeded is invoked, or
|
||||
* The authentication lifecycle ends when either:
|
||||
* 1) A fingerprint is accepted, and ISessionCallback#onAuthenticationSucceeded is invoked.
|
||||
* 2) Any non-recoverable error occurs (such as lockout). See the full list of
|
||||
* authentication-specific errors in the Error enum.
|
||||
*
|
||||
@@ -162,16 +174,28 @@ interface ISession {
|
||||
* must be set with the operationId passed in during #authenticate. If the sensor is NOT
|
||||
* SensorStrength::STRONG, the HardwareAuthToken MUST be null.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onAuthenticationSucceeded
|
||||
*
|
||||
* Other applicable callbacks:
|
||||
* - ISessionCallback#onAcquired
|
||||
* - ISessionCallback#onAuthenticationFailed
|
||||
* - ISessionCallback#onLockoutTimed
|
||||
* - ISessionCallback#onLockoutPermanent
|
||||
*
|
||||
* @param operationId For sensors configured as SensorStrength::STRONG, this must be used ONLY
|
||||
* upon successful authentication and wrapped in the HardwareAuthToken's
|
||||
* "challenge" field and sent to the framework via
|
||||
* ISessionCallback#onAuthenticated. The operationId is an opaque identifier
|
||||
* created from a separate secure subsystem such as, but not limited to
|
||||
* KeyStore/KeyMaster. The HardwareAuthToken can then be used as an
|
||||
* attestation for the provided operation. For example, this is used
|
||||
* to unlock biometric-bound auth-per-use keys (see
|
||||
* ISessionCallback#onAuthenticationSucceeded. The operationId is an opaque
|
||||
* identifier created from a separate secure subsystem such as, but not
|
||||
* limited to KeyStore/KeyMaster. The HardwareAuthToken can then be used as
|
||||
* an attestation for the provided operation. For example, this is used to
|
||||
* unlock biometric-bound auth-per-use keys (see
|
||||
* setUserAuthenticationParameters in KeyGenParameterSpec.Builder and
|
||||
* KeyProtection.Builder.
|
||||
* KeyProtection.Builder).
|
||||
* @return ICancellationSignal An object that can be used by the framework to cancel this
|
||||
* operation.
|
||||
*/
|
||||
ICancellationSignal authenticate(in long operationId);
|
||||
|
||||
@@ -179,44 +203,52 @@ interface ISession {
|
||||
* detectInteraction:
|
||||
*
|
||||
* A request to start looking for fingerprints without performing matching. Must only be called
|
||||
* if SensorProps#supportsDetectInteraction is true. If invoked on implementations that do not
|
||||
* support this functionality, the HAL must respond with ISession#onError(UNABLE_TO_PROCESS, 0).
|
||||
* if SensorProps#supportsDetectInteraction is true. If invoked on HALs that do not support this
|
||||
* functionality, the HAL must respond with ISession#onError(UNABLE_TO_PROCESS, 0).
|
||||
*
|
||||
* The framework will use this method in cases where determing user presence is required, but
|
||||
* identifying/authentication is not. For example, when the device is encrypted (first boot) or
|
||||
* in lockdown mode.
|
||||
* The framework will use this operation in cases where determining user presence is required,
|
||||
* but identifying/authenticating is not. For example, when the device is encrypted (first boot)
|
||||
* or in lockdown mode.
|
||||
*
|
||||
* At any point during detectInteraction, if a non-recoverable error occurs, the HAL must notify
|
||||
* the framework via ISessionCallback#onError with the applicable error.
|
||||
*
|
||||
* The implementation must only check for a fingerprint-like image was detected (e.g. to
|
||||
* minimize interactions due to non-fingerprint objects), and the lockout counter must not
|
||||
* be modified.
|
||||
* The HAL must only check whether a fingerprint-like image was detected (e.g. to minimize
|
||||
* interactions due to non-fingerprint objects), and the lockout counter must not be modified.
|
||||
*
|
||||
* Upon detecting any fingerprint, the implementation must invoke
|
||||
* ISessionCallback#onInteractionDetected.
|
||||
* Upon detecting any fingerprint, the HAL must invoke ISessionCallback#onInteractionDetected.
|
||||
*
|
||||
* The lifecycle of this operation ends when either
|
||||
* The lifecycle of this operation ends when either:
|
||||
* 1) Any fingerprint is detected and the framework is notified via
|
||||
* ISessionCallback#onInteractiondetected
|
||||
* 2) The operation was cancelled by the framework (see ICancellationSignal)
|
||||
* 3) The HAL ends the operation, for example when a subsequent operation pre-empts this one.
|
||||
* ISessionCallback#onInteractionDetected.
|
||||
* 2) An error occurs, for example Error::TIMEOUT.
|
||||
*
|
||||
* Note that if the operation is canceled, the implementation must notify the framework via
|
||||
* Note that if the operation is canceled, the HAL must notify the framework via
|
||||
* ISessionCallback#onError with Error::CANCELED.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onError
|
||||
* - ISessionCallback#onInteractionDetected
|
||||
*
|
||||
* Other applicable callbacks:
|
||||
* - ISessionCallback#onAcquired
|
||||
*
|
||||
* @return ICancellationSignal An object that can be used by the framework to cancel this
|
||||
* operation.
|
||||
*/
|
||||
ICancellationSignal detectInteraction();
|
||||
|
||||
/*
|
||||
* enumerateEnrollments:
|
||||
*
|
||||
* A request to enumerate (list) the enrollments for this (sensorId, userId) pair. The
|
||||
* framework typically uses this to ensure that its cache is in sync with the HAL.
|
||||
* A request to enumerate (list) the enrollments for this (sensorId, userId) pair. The framework
|
||||
* typically uses this to ensure that its cache is in sync with the HAL.
|
||||
*
|
||||
* The implementation must then notify the framework with a list of enrollments applicable
|
||||
* for the current session via ISessionCallback#onEnrollmentsEnumerated.
|
||||
* The HAL must then notify the framework with a list of enrollments applicable for the current
|
||||
* session via ISessionCallback#onEnrollmentsEnumerated.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onEnrollmentsEnumerated
|
||||
*/
|
||||
void enumerateEnrollments();
|
||||
|
||||
@@ -226,8 +258,12 @@ interface ISession {
|
||||
* A request to remove the enrollments for this (sensorId, userId) pair.
|
||||
*
|
||||
* After removing the enrollmentIds from everywhere necessary (filesystem, secure subsystems,
|
||||
* etc), the implementation must notify the framework via ISessionCallback#onEnrollmentsRemoved.
|
||||
* etc), the HAL must notify the framework via ISessionCallback#onEnrollmentsRemoved.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onEnrollmentsRemoved
|
||||
*
|
||||
* @param enrollmentIds a list of enrollments that should be removed.
|
||||
*/
|
||||
void removeEnrollments(in int[] enrollmentIds);
|
||||
|
||||
@@ -240,15 +276,15 @@ interface ISession {
|
||||
* The following only applies to sensors that are configured as SensorStrength::STRONG.
|
||||
*
|
||||
* The authenticatorId is a (sensorId, user)-specific identifier which can be used during key
|
||||
* generation and key import to to associate a key (in KeyStore / KeyMaster) with the current
|
||||
* set of enrolled fingerprints. For example, the following public Android APIs allow for keys
|
||||
* to be invalidated when the user adds a new enrollment after the key was created:
|
||||
* generation and import to associate the key (in KeyStore / KeyMaster) with the current set of
|
||||
* enrolled fingerprints. For example, the following public Android APIs allow for keys to be
|
||||
* invalidated when the user adds a new enrollment after the key was created:
|
||||
* KeyGenParameterSpec.Builder.setInvalidatedByBiometricEnrollment and
|
||||
* KeyProtection.Builder.setInvalidatedByBiometricEnrollment.
|
||||
*
|
||||
* In addition, upon successful fingerprint authentication, the signed HAT that is returned to
|
||||
* the framework via ISessionCallback#onAuthenticated must contain this identifier in the
|
||||
* authenticatorId field.
|
||||
* the framework via ISessionCallback#onAuthenticationSucceeded must contain this identifier in
|
||||
* the authenticatorId field.
|
||||
*
|
||||
* Returns an entropy-encoded random identifier associated with the current set of enrollments
|
||||
* via ISessionCallback#onAuthenticatorIdRetrieved. The authenticatorId
|
||||
@@ -257,20 +293,21 @@ interface ISession {
|
||||
* 3) MUST not change if a fingerprint is deleted.
|
||||
* 4) MUST be an entropy-encoded random number
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onAuthenticatorIdRetrieved
|
||||
*/
|
||||
void getAuthenticatorId();
|
||||
|
||||
/**
|
||||
* invalidateAuthenticatorId:
|
||||
*
|
||||
* This method only applies to sensors that are configured as SensorStrength::STRONG. If invoked
|
||||
* by the framework for sensor of other strengths, the HAL should immediately invoke
|
||||
* This operation only applies to sensors that are configured as SensorStrength::STRONG. If
|
||||
* invoked by the framework for sensors of other strengths, the HAL should immediately invoke
|
||||
* ISessionCallback#onAuthenticatorIdInvalidated.
|
||||
*
|
||||
* The following only applies to sensors that are configured as SensorStrength::STRONG.
|
||||
*
|
||||
* When invoked by the framework, the implementation must perform the following sequence of
|
||||
* events:
|
||||
* When invoked by the framework, the HAL must perform the following sequence of events:
|
||||
* 1) Update the authenticatorId with a new entropy-encoded random number
|
||||
* 2) Persist the new authenticatorId to non-ephemeral storage
|
||||
* 3) Notify the framework that the above is completed, via
|
||||
@@ -278,23 +315,25 @@ interface ISession {
|
||||
*
|
||||
* A practical use case of invalidation would be when the user adds a new enrollment to a sensor
|
||||
* managed by a different HAL instance. The public android.security.keystore APIs bind keys to
|
||||
* "all biometrics" rather than "fingerprint-only" or "face-only" (see #getAuthenticatorId
|
||||
* for more details). As such, the framework would coordinate invalidation across multiple
|
||||
* biometric HALs as necessary.
|
||||
* "all biometrics" rather than "fingerprint-only" or "face-only" (see #getAuthenticatorId for
|
||||
* more details). As such, the framework would coordinate invalidation across multiple biometric
|
||||
* HALs as necessary.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onAuthenticatorIdInvalidated
|
||||
*/
|
||||
void invalidateAuthenticatorId();
|
||||
|
||||
/**
|
||||
* resetLockout:
|
||||
*
|
||||
* Requests the implementation to clear the lockout counter. Upon receiving this request, the
|
||||
* implementation must perform the following:
|
||||
* Requests the HAL to clear the lockout counter. Upon receiving this request, the HAL must
|
||||
* perform the following:
|
||||
* 1) Verify the authenticity and integrity of the provided HAT
|
||||
* 2) Verify that the timestamp provided within the HAT is relatively recent (e.g. on the
|
||||
* order of minutes, not hours).
|
||||
* If either of the checks fail, the HAL must invoke ISessionCallback#onError with
|
||||
* Error::UNABLE_TO_PROCESS and return to the idling state.
|
||||
* Error::UNABLE_TO_PROCESS.
|
||||
*
|
||||
* Upon successful verification, the HAL must clear the lockout counter and notify the framework
|
||||
* via ISessionCallback#onLockoutCleared.
|
||||
@@ -325,6 +364,9 @@ interface ISession {
|
||||
* See the Android CDD section 7.3.10 for the full set of lockout and rate-limiting
|
||||
* requirements.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onLockoutCleared
|
||||
*
|
||||
* @param hat HardwareAuthToken See above documentation.
|
||||
*/
|
||||
void resetLockout(in HardwareAuthToken hat);
|
||||
@@ -343,6 +385,8 @@ interface ISession {
|
||||
* All sessions must be explicitly closed. Calling IFingerprint#createSession while there is an
|
||||
* active session is considered an error.
|
||||
*
|
||||
* Callbacks that signify the end of this operation's lifecycle:
|
||||
* - ISessionCallback#onSessionClosed
|
||||
*/
|
||||
void close();
|
||||
|
||||
@@ -353,16 +397,16 @@ interface ISession {
|
||||
/**
|
||||
* onPointerDown:
|
||||
*
|
||||
* This method only applies to sensors that are configured as
|
||||
* This operation only applies to sensors that are configured as
|
||||
* FingerprintSensorType::UNDER_DISPLAY_*. If invoked erroneously by the framework for sensors
|
||||
* of other types, the HAL must treat this as a no-op and return immediately.
|
||||
*
|
||||
* For sensors of type FingerprintSensorType::UNDER_DISPLAY_*, this method is used to notify the
|
||||
* HAL of display touches. This method can be invoked when the HAL is performing any one of:
|
||||
* ISession#authenticate, ISession#enroll, ISession#detectInteraction.
|
||||
* This operation is used to notify the HAL of display touches. This operation can be invoked
|
||||
* when the HAL is performing any one of: ISession#authenticate, ISession#enroll,
|
||||
* ISession#detectInteraction.
|
||||
*
|
||||
* Note that the framework will only invoke this method if the event occurred on the display on
|
||||
* which this sensor is located.
|
||||
* Note that the framework will only invoke this operation if the event occurred on the display
|
||||
* on which this sensor is located.
|
||||
*
|
||||
* Note that for sensors which require illumination such as
|
||||
* FingerprintSensorType::UNDER_DISPLAY_OPTICAL, and where illumination is handled below the
|
||||
@@ -379,10 +423,13 @@ interface ISession {
|
||||
/**
|
||||
* onPointerUp:
|
||||
*
|
||||
* This method only applies to sensors that are configured as
|
||||
* This operation only applies to sensors that are configured as
|
||||
* FingerprintSensorType::UNDER_DISPLAY_*. If invoked for sensors of other types, the HAL must
|
||||
* treat this as a no-op and return immediately.
|
||||
*
|
||||
* This operation can be invoked when the HAL is performing any one of: ISession#authenticate,
|
||||
* ISession#enroll, ISession#detectInteraction.
|
||||
*
|
||||
* @param pointerId See android.view.MotionEvent#getPointerId
|
||||
*/
|
||||
void onPointerUp(in int pointerId);
|
||||
@@ -390,12 +437,15 @@ interface ISession {
|
||||
/*
|
||||
* onUiReady:
|
||||
*
|
||||
* This method only applies to sensors that are configured as
|
||||
* This operation only applies to sensors that are configured as
|
||||
* FingerprintSensorType::UNDER_DISPLAY_OPTICAL. If invoked for sensors of other types, the HAL
|
||||
* must treat this as a no-op and return immediately.
|
||||
*
|
||||
* This operation can be invoked when the HAL is performing any one of: ISession#authenticate,
|
||||
* ISession#enroll, ISession#detectInteraction.
|
||||
*
|
||||
* For FingerprintSensorType::UNDER_DISPLAY_OPTICAL where illumination is handled above the
|
||||
* HAL, the framework will invoke this method to notify that the illumination has started.
|
||||
* HAL, the framework will invoke this operation to notify when the illumination is showing.
|
||||
*/
|
||||
void onUiReady();
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ interface ISessionCallback {
|
||||
|
||||
/**
|
||||
* This method must only be used to notify the framework during the following operations:
|
||||
* 1) ISession#enroll
|
||||
* 2) ISession#authenticate
|
||||
* 3) ISession#detectInteraction
|
||||
* - ISession#enroll
|
||||
* - ISession#authenticate
|
||||
* - ISession#detectInteraction
|
||||
*
|
||||
* These messages may be used to provide user guidance multiple times if necessary per
|
||||
* operation.
|
||||
* These messages may be used to provide user guidance multiple times per operation if
|
||||
* necessary.
|
||||
*
|
||||
* @param info See the AcquiredInfo enum.
|
||||
* @param vendorCode Only valid if info == AcquiredInfo::VENDOR. The vendorCode must be used to
|
||||
@@ -51,18 +51,18 @@ interface ISessionCallback {
|
||||
|
||||
/**
|
||||
* This method must only be used to notify the framework during the following operations:
|
||||
* 1) ISession#enroll
|
||||
* 2) ISession#authenticate
|
||||
* 3) ISession#detectInteraction
|
||||
* 4) ISession#invalidateAuthenticatorId
|
||||
* 5) ISession#resetLockout
|
||||
* - ISession#enroll
|
||||
* - ISession#authenticate
|
||||
* - ISession#detectInteraction
|
||||
* - ISession#invalidateAuthenticatorId
|
||||
* - ISession#resetLockout
|
||||
*
|
||||
* These messages may be used to notify the framework or user that a non-recoverable error
|
||||
* has occurred. The operation is finished, and the HAL can proceed with the next operation
|
||||
* or return to the idling state.
|
||||
*
|
||||
* Note that cancellation (see common::ICancellationSignal) and preemption must be followed with
|
||||
* an Error::CANCELED message.
|
||||
* Note that cancellation (see common::ICancellationSignal) must be followed with an
|
||||
* Error::CANCELED message.
|
||||
*
|
||||
* @param error See the Error enum.
|
||||
* @param vendorCode Only valid if error == Error::VENDOR. The vendorCode must be used to index
|
||||
@@ -100,8 +100,8 @@ interface ISessionCallback {
|
||||
* This method must only be used to notify the framework during ISession#authenticate.
|
||||
*
|
||||
* Used to notify the framework upon rejected attempts. Note that the authentication
|
||||
* lifecycle ends when either 1) a fingerprint is accepted, or 2) an occurred. The
|
||||
* authentication lifecycle does NOT end when a fingerprint is rejected.
|
||||
* lifecycle ends when either 1) a fingerprint is accepted, or 2) an error occurred.
|
||||
* The authentication lifecycle does NOT end when a fingerprint is rejected.
|
||||
*/
|
||||
void onAuthenticationFailed();
|
||||
|
||||
|
||||
@@ -17,14 +17,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <random>
|
||||
|
||||
namespace aidl::android::hardware::biometrics::fingerprint {
|
||||
|
||||
class FakeFingerprintEngine {
|
||||
public:
|
||||
FakeFingerprintEngine() : mRandom(std::mt19937::default_seed) {}
|
||||
|
||||
void generateChallengeImpl(ISessionCallback* cb) {
|
||||
LOG(INFO) << "generateChallengeImpl";
|
||||
cb->onChallengeGenerated(0 /* challenge */);
|
||||
std::uniform_int_distribution<int64_t> dist;
|
||||
auto challenge = dist(mRandom);
|
||||
cb->onChallengeGenerated(challenge);
|
||||
}
|
||||
|
||||
void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
|
||||
@@ -32,8 +37,13 @@ class FakeFingerprintEngine {
|
||||
cb->onChallengeRevoked(challenge);
|
||||
}
|
||||
|
||||
void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/) {
|
||||
void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat) {
|
||||
LOG(INFO) << "enrollImpl";
|
||||
// Do proper HAT verification in the real implementation.
|
||||
if (hat.mac.empty()) {
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
cb->onEnrollmentProgress(0 /* enrollmentId */, 0 /* remaining */);
|
||||
}
|
||||
|
||||
@@ -71,6 +81,8 @@ class FakeFingerprintEngine {
|
||||
LOG(INFO) << "resetLockoutImpl";
|
||||
cb->onLockoutCleared();
|
||||
}
|
||||
|
||||
std::mt19937 mRandom;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::biometrics::fingerprint
|
||||
|
||||
@@ -35,13 +35,19 @@ constexpr int kUserId = 0;
|
||||
|
||||
class SessionCallback : public BnSessionCallback {
|
||||
public:
|
||||
explicit SessionCallback(std::promise<void>&& promise) : mPromise(std::move(promise)) {}
|
||||
|
||||
ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
|
||||
ndk::ScopedAStatus onChallengeGenerated(int64_t challenge) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnChallengeGeneratedInvoked = true;
|
||||
mGeneratedChallenge = challenge;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onChallengeRevoked(int64_t /*challenge*/) override {
|
||||
ndk::ScopedAStatus onChallengeRevoked(int64_t challenge) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnChallengeRevokedInvoked = true;
|
||||
mRevokedChallenge = challenge;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -49,7 +55,11 @@ class SessionCallback : public BnSessionCallback {
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onError(Error /*error*/, int32_t /*vendorCode*/) override {
|
||||
ndk::ScopedAStatus onError(Error error, int32_t /*vendorCode*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mError = error;
|
||||
mOnErrorInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -77,63 +87,203 @@ class SessionCallback : public BnSessionCallback {
|
||||
|
||||
ndk::ScopedAStatus onEnrollmentsEnumerated(
|
||||
const std::vector<int32_t>& /*enrollmentIds*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnEnrollmentsEnumeratedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onEnrollmentsRemoved(
|
||||
const std::vector<int32_t>& /*enrollmentIds*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnEnrollmentsRemovedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t /*authenticatorId*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnAuthenticatorIdRetrievedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t /*newAuthenticatorId*/) override {
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnAuthenticatorIdInvalidatedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus onSessionClosed() override {
|
||||
mPromise.set_value();
|
||||
auto lock = std::lock_guard{mMutex};
|
||||
mOnSessionClosedInvoked = true;
|
||||
mCv.notify_one();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
std::promise<void> mPromise;
|
||||
std::mutex mMutex;
|
||||
std::condition_variable mCv;
|
||||
Error mError = Error::UNKNOWN;
|
||||
int64_t mGeneratedChallenge = 0;
|
||||
int64_t mRevokedChallenge = 0;
|
||||
bool mOnChallengeGeneratedInvoked = false;
|
||||
bool mOnChallengeRevokedInvoked = false;
|
||||
bool mOnErrorInvoked = false;
|
||||
bool mOnEnrollmentsEnumeratedInvoked = false;
|
||||
bool mOnEnrollmentsRemovedInvoked = false;
|
||||
bool mOnAuthenticatorIdRetrievedInvoked = false;
|
||||
bool mOnAuthenticatorIdInvalidatedInvoked = false;
|
||||
bool mOnSessionClosedInvoked = false;
|
||||
};
|
||||
|
||||
class Fingerprint : public testing::TestWithParam<std::string> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
|
||||
ASSERT_NE(binder, nullptr);
|
||||
mHal = IFingerprint::fromBinder(ndk::SpAIBinder(binder));
|
||||
// Prepare the callback.
|
||||
mCb = ndk::SharedRefBase::make<SessionCallback>();
|
||||
|
||||
int retries = 0;
|
||||
bool isOk = false;
|
||||
// If the first attempt to create a session fails, we try to create a session again. The
|
||||
// first attempt might fail if the framework already has an active session. The AIDL
|
||||
// contract doesn't allow to create a new session without closing the old one. However, we
|
||||
// can't close the framework's session from VTS. The expectation here is that the HAL will
|
||||
// crash after the first illegal attempt to create a session, then it will restart, and then
|
||||
// we'll be able to create a session.
|
||||
do {
|
||||
// Get an instance of the HAL.
|
||||
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
|
||||
ASSERT_NE(binder, nullptr);
|
||||
mHal = IFingerprint::fromBinder(ndk::SpAIBinder(binder));
|
||||
|
||||
// Create a session.
|
||||
isOk = mHal->createSession(kSensorId, kUserId, mCb, &mSession).isOk();
|
||||
++retries;
|
||||
} while (!isOk && retries < 2);
|
||||
|
||||
ASSERT_TRUE(isOk);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Close the mSession.
|
||||
ASSERT_TRUE(mSession->close().isOk());
|
||||
|
||||
// Make sure the mSession is closed.
|
||||
auto lock = std::unique_lock<std::mutex>(mCb->mMutex);
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnSessionClosedInvoked; });
|
||||
}
|
||||
|
||||
std::shared_ptr<IFingerprint> mHal;
|
||||
std::shared_ptr<SessionCallback> mCb;
|
||||
std::shared_ptr<ISession> mSession;
|
||||
};
|
||||
|
||||
TEST_P(Fingerprint, AuthenticateTest) {
|
||||
auto promise = std::promise<void>{};
|
||||
auto future = promise.get_future();
|
||||
// Prepare the callback.
|
||||
auto cb = ndk::SharedRefBase::make<SessionCallback>(std::move(promise));
|
||||
TEST_P(Fingerprint, GetSensorPropsWorksTest) {
|
||||
std::vector<SensorProps> sensorProps;
|
||||
|
||||
// Create a session
|
||||
std::shared_ptr<ISession> session;
|
||||
ASSERT_TRUE(mHal->createSession(kSensorId, kUserId, cb, &session).isOk());
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mHal->getSensorProps(&sensorProps).isOk());
|
||||
|
||||
// Call authenticate
|
||||
// Make sure the sensorProps aren't empty.
|
||||
ASSERT_FALSE(sensorProps.empty());
|
||||
ASSERT_FALSE(sensorProps[0].commonProps.componentInfo.empty());
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, EnrollWithBadHatResultsInErrorTest) {
|
||||
// Call the method.
|
||||
auto hat = keymaster::HardwareAuthToken{};
|
||||
std::shared_ptr<common::ICancellationSignal> cancellationSignal;
|
||||
ASSERT_TRUE(session->authenticate(-1 /* operationId */, &cancellationSignal).isOk());
|
||||
ASSERT_TRUE(mSession->enroll(hat, &cancellationSignal).isOk());
|
||||
|
||||
// Get the results
|
||||
// TODO(b/166799066): test authenticate.
|
||||
// Make sure an error is returned.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnErrorInvoked; });
|
||||
}
|
||||
|
||||
// Close the session
|
||||
ASSERT_TRUE(session->close().isOk());
|
||||
auto status = future.wait_for(1s);
|
||||
ASSERT_EQ(status, std::future_status::ready);
|
||||
TEST_P(Fingerprint, GenerateChallengeProducesUniqueChallengesTest) {
|
||||
static constexpr int kIterations = 100;
|
||||
|
||||
auto challenges = std::set<int>{};
|
||||
for (unsigned int i = 0; i < kIterations; ++i) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->generateChallenge().isOk());
|
||||
|
||||
// Check that the generated challenge is unique and not 0.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeGeneratedInvoked; });
|
||||
ASSERT_NE(mCb->mGeneratedChallenge, 0);
|
||||
ASSERT_EQ(challenges.find(mCb->mGeneratedChallenge), challenges.end());
|
||||
|
||||
challenges.insert(mCb->mGeneratedChallenge);
|
||||
mCb->mOnChallengeGeneratedInvoked = false;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, RevokeChallengeWorksForNonexistentChallengeTest) {
|
||||
const int64_t nonexistentChallenge = 123;
|
||||
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->revokeChallenge(nonexistentChallenge).isOk());
|
||||
|
||||
// Check that the challenge is revoked and matches the requested challenge.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeRevokedInvoked; });
|
||||
ASSERT_EQ(mCb->mRevokedChallenge, nonexistentChallenge);
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, RevokeChallengeWorksForExistentChallengeTest) {
|
||||
// Generate a challenge.
|
||||
ASSERT_TRUE(mSession->generateChallenge().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeGeneratedInvoked; });
|
||||
lock.unlock();
|
||||
|
||||
// Revoke the challenge.
|
||||
ASSERT_TRUE(mSession->revokeChallenge(mCb->mGeneratedChallenge).isOk());
|
||||
|
||||
// Check that the challenge is revoked and matches the requested challenge.
|
||||
lock.lock();
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnChallengeRevokedInvoked; });
|
||||
ASSERT_EQ(mCb->mRevokedChallenge, mCb->mGeneratedChallenge);
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, EnumerateEnrollmentsWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->enumerateEnrollments().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnEnrollmentsEnumeratedInvoked; });
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, RemoveEnrollmentsWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->removeEnrollments({}).isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnEnrollmentsRemovedInvoked; });
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, GetAuthenticatorIdWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->getAuthenticatorId().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnAuthenticatorIdRetrievedInvoked; });
|
||||
}
|
||||
|
||||
TEST_P(Fingerprint, InvalidateAuthenticatorIdWorksTest) {
|
||||
// Call the method.
|
||||
ASSERT_TRUE(mSession->invalidateAuthenticatorId().isOk());
|
||||
|
||||
// Wait for the result.
|
||||
auto lock = std::unique_lock{mCb->mMutex};
|
||||
mCb->mCv.wait(lock, [this] { return mCb->mOnAuthenticatorIdInvalidatedInvoked; });
|
||||
}
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Fingerprint);
|
||||
|
||||
@@ -32,10 +32,14 @@ namespace implementation {
|
||||
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
|
||||
using ::android::hardware::Void;
|
||||
|
||||
// Here the buffer size is based on SBC
|
||||
static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
|
||||
static constexpr uint32_t kPcmFrameCount = 128;
|
||||
// SBC is 128, and here choose the LCM of 16, 24, and 32
|
||||
static constexpr uint32_t kPcmFrameCount = 96;
|
||||
static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
|
||||
static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
|
||||
// The max counts by 1 tick (20ms) for SBC is about 7. Since using 96 for the
|
||||
// PCM counts, here we just choose a greater number
|
||||
static constexpr uint32_t kRtpFrameCount = 10;
|
||||
static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
|
||||
static constexpr uint32_t kBufferCount = 2; // double buffer
|
||||
static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
|
||||
|
||||
@@ -34,10 +34,14 @@ using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
||||
|
||||
// Here the buffer size is based on SBC
|
||||
static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
|
||||
static constexpr uint32_t kPcmFrameCount = 128;
|
||||
// SBC is 128, and here we choose the LCM of 16, 24, and 32
|
||||
static constexpr uint32_t kPcmFrameCount = 96;
|
||||
static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
|
||||
static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
|
||||
// The max counts by 1 tick (20ms) for SBC is about 7. Since using 96 for the
|
||||
// PCM counts, here we just choose a greater number
|
||||
static constexpr uint32_t kRtpFrameCount = 10;
|
||||
static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
|
||||
static constexpr uint32_t kBufferCount = 2; // double buffer
|
||||
static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
|
||||
|
||||
@@ -42,7 +42,7 @@ struct Stream {
|
||||
/**
|
||||
* The surface group id used for multi-resolution output streams.
|
||||
*
|
||||
* This works simliar to the surfaceGroupId of OutputConfiguration in the
|
||||
* This works similar to the surfaceGroupId of OutputConfiguration in the
|
||||
* public API, with the exception that this is for multi-resolution image
|
||||
* reader and is used by the camera HAL to choose a target stream within
|
||||
* the same group to which images are written. All streams in the same group
|
||||
|
||||
@@ -686,7 +686,9 @@ enum CameraMetadataTag : uint32_t {
|
||||
/** android.jpeg.maxSize [static, int32, system]
|
||||
*
|
||||
* <p>Maximum size in bytes for the compressed
|
||||
* JPEG buffer</p>
|
||||
* JPEG buffer, in default sensor pixel mode (see ANDROID_SENSOR_PIXEL_MODE)</p>
|
||||
*
|
||||
* @see ANDROID_SENSOR_PIXEL_MODE
|
||||
*/
|
||||
ANDROID_JPEG_MAX_SIZE,
|
||||
|
||||
|
||||
@@ -230,10 +230,10 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
int getCameraDeviceVersion(const hidl_string& deviceName,
|
||||
const hidl_string &providerType) {
|
||||
int getCameraDeviceVersionAndId(const hidl_string& deviceName,
|
||||
const hidl_string &providerType, std::string* id) {
|
||||
std::string version;
|
||||
bool match = matchDeviceName(deviceName, providerType, &version, nullptr);
|
||||
bool match = matchDeviceName(deviceName, providerType, &version, id);
|
||||
if (!match) {
|
||||
return -1;
|
||||
}
|
||||
@@ -256,6 +256,11 @@ namespace {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getCameraDeviceVersion(const hidl_string& deviceName,
|
||||
const hidl_string &providerType) {
|
||||
return getCameraDeviceVersionAndId(deviceName, providerType, nullptr);
|
||||
}
|
||||
|
||||
bool parseProviderName(const std::string& name, std::string *type /*out*/,
|
||||
uint32_t *id /*out*/) {
|
||||
if (!type || !id) {
|
||||
@@ -930,6 +935,7 @@ public:
|
||||
camera_metadata_ro_entry* streamConfigs,
|
||||
camera_metadata_ro_entry* maxResolutionStreamConfigs,
|
||||
const camera_metadata_t* staticMetadata);
|
||||
static bool isColorCamera(const camera_metadata_t *metadata);
|
||||
|
||||
static V3_2::DataspaceFlags getDataspace(PixelFormat format);
|
||||
|
||||
@@ -6179,6 +6185,167 @@ TEST_P(CameraHidlTest, configureInjectionStreamsWithSessionParameters) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test the multi-camera API requirement for Google Requirement Freeze S
|
||||
// Note that this requirement can only be partially tested. If a vendor
|
||||
// device doesn't expose a physical camera in any shape or form, there is no way
|
||||
// the test can catch it.
|
||||
TEST_P(CameraHidlTest, grfSMultiCameraTest) {
|
||||
const int socGrfApi = property_get_int32("ro.board.first_api_level", /*default*/ -1);
|
||||
if (socGrfApi < 31 /*S*/) {
|
||||
// Non-GRF devices, or version < 31 Skip
|
||||
ALOGI("%s: socGrfApi level is %d. Skipping", __FUNCTION__, socGrfApi);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test that if more than one color cameras facing the same direction are
|
||||
// supported, there must be at least one logical camera facing that
|
||||
// direction.
|
||||
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
||||
// Front and back facing non-logical color cameras
|
||||
std::set<std::string> frontColorCameras, rearColorCameras;
|
||||
// Front and back facing logical cameras' physical camera Id sets
|
||||
std::set<std::set<std::string>> frontPhysicalIds, rearPhysicalIds;
|
||||
for (const auto& name : cameraDeviceNames) {
|
||||
std::string cameraId;
|
||||
int deviceVersion = getCameraDeviceVersionAndId(name, mProviderType, &cameraId);
|
||||
switch (deviceVersion) {
|
||||
case CAMERA_DEVICE_API_VERSION_3_7:
|
||||
case CAMERA_DEVICE_API_VERSION_3_6:
|
||||
case CAMERA_DEVICE_API_VERSION_3_5:
|
||||
case CAMERA_DEVICE_API_VERSION_3_4:
|
||||
case CAMERA_DEVICE_API_VERSION_3_3:
|
||||
case CAMERA_DEVICE_API_VERSION_3_2: {
|
||||
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
|
||||
ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
|
||||
Return<void> ret;
|
||||
ret = mProvider->getCameraDeviceInterface_V3_x(
|
||||
name, [&](auto status, const auto& device) {
|
||||
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_NE(device, nullptr);
|
||||
device3_x = device;
|
||||
});
|
||||
ASSERT_TRUE(ret.isOk());
|
||||
|
||||
ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
|
||||
|
||||
// Skip if this is not a color camera.
|
||||
if (!CameraHidlTest::isColorCamera(metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check camera facing. Skip if facing is neither FRONT
|
||||
// nor BACK. If this is not a logical camera, only note down
|
||||
// the camera ID, and skip.
|
||||
camera_metadata_ro_entry entry;
|
||||
int retcode = find_camera_metadata_ro_entry(
|
||||
metadata, ANDROID_LENS_FACING, &entry);
|
||||
ASSERT_EQ(retcode, 0);
|
||||
ASSERT_GT(entry.count, 0);
|
||||
uint8_t facing = entry.data.u8[0];
|
||||
bool isLogicalCamera = (isLogicalMultiCamera(metadata) == Status::OK);
|
||||
if (facing == ANDROID_LENS_FACING_FRONT) {
|
||||
if (!isLogicalCamera) {
|
||||
frontColorCameras.insert(cameraId);
|
||||
return;
|
||||
}
|
||||
} else if (facing == ANDROID_LENS_FACING_BACK) {
|
||||
if (!isLogicalCamera) {
|
||||
rearColorCameras.insert(cameraId);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Not FRONT or BACK facing. Skip.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check logical camera's physical camera IDs for color
|
||||
// cameras.
|
||||
std::unordered_set<std::string> physicalCameraIds;
|
||||
Status s = getPhysicalCameraIds(metadata, &physicalCameraIds);
|
||||
ASSERT_EQ(Status::OK, s);
|
||||
if (facing == ANDROID_LENS_FACING_FRONT) {
|
||||
frontPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
|
||||
} else {
|
||||
rearPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
|
||||
}
|
||||
for (const auto& physicalId : physicalCameraIds) {
|
||||
// Skip if the physicalId is publicly available
|
||||
for (auto& deviceName : cameraDeviceNames) {
|
||||
std::string publicVersion, publicId;
|
||||
ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType,
|
||||
&publicVersion, &publicId));
|
||||
if (physicalId == publicId) {
|
||||
// Skip because public Ids will be iterated in outer loop.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto castResult = device::V3_5::ICameraDevice::castFrom(device3_x);
|
||||
ASSERT_TRUE(castResult.isOk());
|
||||
::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice>
|
||||
device3_5 = castResult;
|
||||
ASSERT_NE(device3_5, nullptr);
|
||||
|
||||
// Check camera characteristics for hidden camera id
|
||||
Return<void> ret = device3_5->getPhysicalCameraCharacteristics(
|
||||
physicalId, [&](auto status, const auto& chars) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
const camera_metadata_t* physicalMetadata =
|
||||
(camera_metadata_t*)chars.data();
|
||||
|
||||
if (CameraHidlTest::isColorCamera(physicalMetadata)) {
|
||||
if (facing == ANDROID_LENS_FACING_FRONT) {
|
||||
frontColorCameras.insert(physicalId);
|
||||
} else if (facing == ANDROID_LENS_FACING_BACK) {
|
||||
rearColorCameras.insert(physicalId);
|
||||
}
|
||||
}
|
||||
});
|
||||
ASSERT_TRUE(ret.isOk());
|
||||
}
|
||||
});
|
||||
ASSERT_TRUE(ret.isOk());
|
||||
} break;
|
||||
case CAMERA_DEVICE_API_VERSION_1_0: {
|
||||
// Not applicable
|
||||
} break;
|
||||
default: {
|
||||
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
||||
ADD_FAILURE();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are more than one color cameras facing one direction, a logical
|
||||
// multi-camera must be defined consisting of all color cameras facing that
|
||||
// direction.
|
||||
if (frontColorCameras.size() > 1) {
|
||||
bool hasFrontLogical = false;
|
||||
for (const auto& physicalIds : frontPhysicalIds) {
|
||||
if (std::includes(physicalIds.begin(), physicalIds.end(),
|
||||
frontColorCameras.begin(), frontColorCameras.end())) {
|
||||
hasFrontLogical = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(hasFrontLogical);
|
||||
}
|
||||
if (rearColorCameras.size() > 1) {
|
||||
bool hasRearLogical = false;
|
||||
for (const auto& physicalIds : rearPhysicalIds) {
|
||||
if (std::includes(physicalIds.begin(), physicalIds.end(),
|
||||
rearColorCameras.begin(), rearColorCameras.end())) {
|
||||
hasRearLogical = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(hasRearLogical);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve all valid output stream resolutions from the camera
|
||||
// static characteristics.
|
||||
Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t* staticMeta,
|
||||
@@ -6651,6 +6818,23 @@ Status CameraHidlTest::isMonochromeCamera(const camera_metadata_t *staticMeta) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CameraHidlTest::isColorCamera(const camera_metadata_t *metadata) {
|
||||
camera_metadata_ro_entry entry;
|
||||
int retcode = find_camera_metadata_ro_entry(
|
||||
metadata, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
||||
if ((0 == retcode) && (entry.count > 0)) {
|
||||
bool isBackwardCompatible = (std::find(entry.data.u8, entry.data.u8 + entry.count,
|
||||
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) !=
|
||||
entry.data.u8 + entry.count);
|
||||
bool isMonochrome = (std::find(entry.data.u8, entry.data.u8 + entry.count,
|
||||
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) !=
|
||||
entry.data.u8 + entry.count);
|
||||
bool isColor = isBackwardCompatible && !isMonochrome;
|
||||
return isColor;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the reprocess input-output format map from the static
|
||||
// camera characteristics.
|
||||
Status CameraHidlTest::getZSLInputOutputMap(camera_metadata_t *staticMeta,
|
||||
|
||||
@@ -768,6 +768,8 @@ a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardwar
|
||||
98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types
|
||||
|
||||
# ABI preserving changes to HALs during Android S
|
||||
# b/193346383
|
||||
93d29fbe2fcc5e4e053a9db7c9abbd9190c46b85b443f2698a3460db2ee76c8d android.hardware.camera.metadata@3.2::types
|
||||
159a0069336035852e9eca6354b86b7990680d1b239f23ef2f631b01807c4cb9 android.hardware.camera.metadata@3.5::types
|
||||
e042522daa4b5f7fd4a0a19bcdadb93c79a1b04c09ef2c9813a3a8941032f3f5 android.hardware.contexthub@1.0::IContexthub
|
||||
c2f64133b83ede65c9939ef97ab5bd867b73faf3dba0e7e69f77c3c43d9e487e android.hardware.contexthub@1.0::IContexthubCallback
|
||||
@@ -831,6 +833,7 @@ e3865e74cb1a6e6afd38c7aa84115cb109ce47b972132de5242bc3838d2771f6 android.hardwar
|
||||
b3caf524c46a47d67e6453a34419e1881942d059e146cda740502670e9a752c3 android.hardware.automotive.vehicle@2.0::IVehicle
|
||||
7ce8728b27600e840cacf0a832f6942819fe535f9d3797ae052d5eef5065921c android.hardware.automotive.vehicle@2.0::IVehicleCallback
|
||||
b525e91d886379c13588f4975bb04d625d46e1f41b4453792c4b2db1e7ff4340 android.hardware.biometrics.fingerprint@2.3::IBiometricsFingerprint
|
||||
7a78e9963bec0b071e7d46928c6100e2174270892d3f15a1eaad074997adf279 android.hardware.biometrics.fingerprint@2.3::IBiometricsFingerprint # Added for b/160189286 for Android S
|
||||
4baf8e0eca4aa896cc9ceb7bb676aaf4fa21372ef8b49eed68eced1221c3dc0d android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvider
|
||||
d417a9212c8f96e3a06a2f221c8c5756c765355b2b81de2b2a65d4c9eee85401 android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory
|
||||
c17d9e27abd37ae5a8ff8da08fc5c9b13a264670feef6bbbc9d3ab1915216130 android.hardware.bluetooth.audio@2.1::types
|
||||
@@ -838,7 +841,7 @@ c17d9e27abd37ae5a8ff8da08fc5c9b13a264670feef6bbbc9d3ab1915216130 android.hardwar
|
||||
1a1dff6e8d25dbc02a69fed3c077dd0782b30331ca3f345848ec52fc67744224 android.hardware.camera.device@3.7::ICameraDevice
|
||||
3be6faa3d11ad9c7ec01a1a0a009cf11cb65d701d109dab37613ce9cfb3cdd60 android.hardware.camera.device@3.7::ICameraDeviceSession
|
||||
3740ec773b2eb8fa6bd8c6e879eedb56c4e4306b88f1c20fa51103d791d871b1 android.hardware.camera.device@3.7::ICameraInjectionSession
|
||||
21f023685571daf46148097d98b89cea353f07e3ed83b2ed5685b23bd136c3ee android.hardware.camera.device@3.7::types
|
||||
d272697484c41bbf76a0924d2aaebf065ce37a822fcb438316eb5dd2d112f052 android.hardware.camera.device@3.7::types
|
||||
e932e7ef95210142e1fd3a4504e1d19bdb1acc988450f1ced543f3401f67855a android.hardware.camera.metadata@3.6::types
|
||||
98ff825a7d37e5ab983502d13cec1f2e5a9cac9b674b6ff1a52bcf540f4e315e android.hardware.camera.provider@2.7::ICameraProvider
|
||||
51fd14005859b16be55872660c34f5d423c77a2abcc5d4bdd5a537c40f32516b android.hardware.camera.provider@2.7::types
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#define LOG_TAG "Gnss"
|
||||
|
||||
#include "Gnss.h"
|
||||
#include <android/hardware/gnss/1.0/types.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "Gnss.h"
|
||||
#include "Constants.h"
|
||||
#include "GnssDebug.h"
|
||||
#include "GnssMeasurement.h"
|
||||
#include "Utils.h"
|
||||
@@ -16,6 +16,7 @@ namespace implementation {
|
||||
|
||||
using ::android::hardware::gnss::common::Utils;
|
||||
using GnssSvFlags = IGnssCallback::GnssSvFlags;
|
||||
using namespace ::android::hardware::gnss::common;
|
||||
|
||||
const uint32_t MIN_INTERVAL_MILLIS = 100;
|
||||
sp<::android::hardware::gnss::V1_1::IGnssCallback> Gnss::sGnssCallback = nullptr;
|
||||
@@ -197,14 +198,21 @@ Return<bool> Gnss::injectBestLocation(const GnssLocation&) {
|
||||
Return<GnssSvStatus> Gnss::getMockSvStatus() const {
|
||||
std::unique_lock<std::recursive_mutex> lock(mGnssConfiguration->getMutex());
|
||||
GnssSvInfo mockGnssSvInfoList[] = {
|
||||
Utils::getMockSvInfoV1_0(3, GnssConstellationType::GPS, 32.5, 59.1, 166.5),
|
||||
Utils::getMockSvInfoV1_0(5, GnssConstellationType::GPS, 27.0, 29.0, 56.5),
|
||||
Utils::getMockSvInfoV1_0(17, GnssConstellationType::GPS, 30.5, 71.0, 77.0),
|
||||
Utils::getMockSvInfoV1_0(26, GnssConstellationType::GPS, 24.1, 28.0, 253.0),
|
||||
Utils::getMockSvInfoV1_0(5, GnssConstellationType::GLONASS, 20.5, 11.5, 116.0),
|
||||
Utils::getMockSvInfoV1_0(17, GnssConstellationType::GLONASS, 21.5, 28.5, 186.0),
|
||||
Utils::getMockSvInfoV1_0(18, GnssConstellationType::GLONASS, 28.3, 38.8, 69.0),
|
||||
Utils::getMockSvInfoV1_0(10, GnssConstellationType::GLONASS, 25.0, 66.0, 247.0)};
|
||||
Utils::getMockSvInfoV1_0(3, GnssConstellationType::GPS, 32.5, 59.1, 166.5,
|
||||
kGpsL1FreqHz),
|
||||
Utils::getMockSvInfoV1_0(5, GnssConstellationType::GPS, 27.0, 29.0, 56.5, kGpsL1FreqHz),
|
||||
Utils::getMockSvInfoV1_0(17, GnssConstellationType::GPS, 30.5, 71.0, 77.0,
|
||||
kGpsL5FreqHz),
|
||||
Utils::getMockSvInfoV1_0(26, GnssConstellationType::GPS, 24.1, 28.0, 253.0,
|
||||
kGpsL5FreqHz),
|
||||
Utils::getMockSvInfoV1_0(5, GnssConstellationType::GLONASS, 20.5, 11.5, 116.0,
|
||||
kGloG1FreqHz),
|
||||
Utils::getMockSvInfoV1_0(17, GnssConstellationType::GLONASS, 21.5, 28.5, 186.0,
|
||||
kGloG1FreqHz),
|
||||
Utils::getMockSvInfoV1_0(18, GnssConstellationType::GLONASS, 28.3, 38.8, 69.0,
|
||||
kGloG1FreqHz),
|
||||
Utils::getMockSvInfoV1_0(10, GnssConstellationType::GLONASS, 25.0, 66.0, 247.0,
|
||||
kGloG1FreqHz)};
|
||||
|
||||
GnssSvStatus svStatus = {.numSvs = sizeof(mockGnssSvInfoList) / sizeof(GnssSvInfo)};
|
||||
for (uint32_t i = 0; i < svStatus.numSvs; i++) {
|
||||
|
||||
@@ -249,35 +249,40 @@ TEST_P(GnssHalTest, TestGnssAntennaInfo) {
|
||||
|
||||
/*
|
||||
* TestGnssSvInfoFields:
|
||||
* Gets 1 location and a GnssSvInfo, and verifies
|
||||
* 1. basebandCN0DbHz is valid.
|
||||
* Gets 1 location and a (non-empty) GnssSvInfo, and verifies basebandCN0DbHz is valid.
|
||||
*/
|
||||
TEST_P(GnssHalTest, TestGnssSvInfoFields) {
|
||||
gnss_cb_->location_cbq_.reset();
|
||||
gnss_cb_->sv_info_list_cbq_.reset();
|
||||
StartAndCheckFirstLocation(/* min_interval_msec= */ 1000, /* low_power_mode= */ false);
|
||||
int location_called_count = gnss_cb_->location_cbq_.calledCount();
|
||||
|
||||
// Tolerate 1 less sv status to handle edge cases in reporting.
|
||||
int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
|
||||
EXPECT_GE(sv_info_list_cbq_size, 0);
|
||||
ALOGD("Observed %d GnssSvStatus, while awaiting one location (%d received)",
|
||||
sv_info_list_cbq_size, location_called_count);
|
||||
gnss_cb_->sv_info_list_cbq_.size(), location_called_count);
|
||||
|
||||
// Get the last sv_info_list
|
||||
std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> sv_info_vec_list;
|
||||
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec_list, sv_info_list_cbq_size, 1);
|
||||
hidl_vec<IGnssCallback_2_1::GnssSvInfo> last_sv_info_list = sv_info_vec_list.back();
|
||||
// Wait for up to kNumSvInfoLists events for kTimeoutSeconds for each event.
|
||||
int kTimeoutSeconds = 2;
|
||||
int kNumSvInfoLists = 4;
|
||||
std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> sv_info_lists;
|
||||
hidl_vec<IGnssCallback_2_1::GnssSvInfo> last_sv_info_list;
|
||||
|
||||
do {
|
||||
EXPECT_GT(gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_lists, kNumSvInfoLists,
|
||||
kTimeoutSeconds),
|
||||
0);
|
||||
last_sv_info_list = sv_info_lists.back();
|
||||
} while (last_sv_info_list.size() == 0);
|
||||
|
||||
ALOGD("last_sv_info size = %d", (int)last_sv_info_list.size());
|
||||
bool nonZeroCn0Found = false;
|
||||
for (auto sv_info : last_sv_info_list) {
|
||||
ASSERT_TRUE(sv_info.basebandCN0DbHz >= 0.0 && sv_info.basebandCN0DbHz <= 65.0);
|
||||
EXPECT_TRUE(sv_info.basebandCN0DbHz >= 0.0 && sv_info.basebandCN0DbHz <= 65.0);
|
||||
if (sv_info.basebandCN0DbHz > 0.0) {
|
||||
nonZeroCn0Found = true;
|
||||
}
|
||||
}
|
||||
// Assert at least one value is non-zero. Zero is ok in status as it's possibly
|
||||
// reporting a searched but not found satellite.
|
||||
ASSERT_TRUE(nonZeroCn0Found);
|
||||
EXPECT_TRUE(nonZeroCn0Found);
|
||||
StopAndClearLocations();
|
||||
}
|
||||
|
||||
|
||||
@@ -265,50 +265,50 @@ V1_0::GnssLocation Utils::getMockLocationV1_0() {
|
||||
}
|
||||
|
||||
hidl_vec<GnssSvInfoV2_1> Utils::getMockSvInfoListV2_1() {
|
||||
GnssSvInfoV1_0 gnssSvInfoV1_0 =
|
||||
Utils::getMockSvInfoV1_0(3, V1_0::GnssConstellationType::GPS, 32.5, 59.1, 166.5);
|
||||
GnssSvInfoV1_0 gnssSvInfoV1_0 = Utils::getMockSvInfoV1_0(3, V1_0::GnssConstellationType::GPS,
|
||||
32.5, 59.1, 166.5, kGpsL1FreqHz);
|
||||
GnssSvInfoV2_0 gnssSvInfoV2_0 =
|
||||
Utils::getMockSvInfoV2_0(gnssSvInfoV1_0, V2_0::GnssConstellationType::GPS);
|
||||
hidl_vec<GnssSvInfoV2_1> gnssSvInfoList = {
|
||||
Utils::getMockSvInfoV2_1(gnssSvInfoV2_0, 27.5),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(5, V1_0::GnssConstellationType::GPS, 27.0,
|
||||
29.0, 56.5),
|
||||
29.0, 56.5, kGpsL1FreqHz),
|
||||
V2_0::GnssConstellationType::GPS),
|
||||
22.0),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(17, V1_0::GnssConstellationType::GPS, 30.5,
|
||||
71.0, 77.0),
|
||||
71.0, 77.0, kGpsL5FreqHz),
|
||||
V2_0::GnssConstellationType::GPS),
|
||||
25.5),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(26, V1_0::GnssConstellationType::GPS, 24.1,
|
||||
28.0, 253.0),
|
||||
28.0, 253.0, kGpsL5FreqHz),
|
||||
V2_0::GnssConstellationType::GPS),
|
||||
19.1),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(5, V1_0::GnssConstellationType::GLONASS,
|
||||
20.5, 11.5, 116.0),
|
||||
20.5, 11.5, 116.0, kGloG1FreqHz),
|
||||
V2_0::GnssConstellationType::GLONASS),
|
||||
15.5),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(17, V1_0::GnssConstellationType::GLONASS,
|
||||
21.5, 28.5, 186.0),
|
||||
21.5, 28.5, 186.0, kGloG1FreqHz),
|
||||
V2_0::GnssConstellationType::GLONASS),
|
||||
16.5),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(18, V1_0::GnssConstellationType::GLONASS,
|
||||
28.3, 38.8, 69.0),
|
||||
28.3, 38.8, 69.0, kGloG1FreqHz),
|
||||
V2_0::GnssConstellationType::GLONASS),
|
||||
25.3),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(10, V1_0::GnssConstellationType::GLONASS,
|
||||
25.0, 66.0, 247.0),
|
||||
25.0, 66.0, 247.0, kGloG1FreqHz),
|
||||
V2_0::GnssConstellationType::GLONASS),
|
||||
20.0),
|
||||
getMockSvInfoV2_1(
|
||||
getMockSvInfoV2_0(getMockSvInfoV1_0(3, V1_0::GnssConstellationType::UNKNOWN,
|
||||
22.0, 35.0, 112.0),
|
||||
22.0, 35.0, 112.0, kIrnssL5FreqHz),
|
||||
V2_0::GnssConstellationType::IRNSS),
|
||||
19.7),
|
||||
};
|
||||
@@ -333,21 +333,23 @@ GnssSvInfoV2_0 Utils::getMockSvInfoV2_0(GnssSvInfoV1_0 gnssSvInfoV1_0,
|
||||
}
|
||||
|
||||
GnssSvInfoV1_0 Utils::getMockSvInfoV1_0(int16_t svid, V1_0::GnssConstellationType type,
|
||||
float cN0DbHz, float elevationDegrees,
|
||||
float azimuthDegrees) {
|
||||
float cN0DbHz, float elevationDegrees, float azimuthDegrees,
|
||||
float carrierFrequencyHz) {
|
||||
GnssSvInfoV1_0 svInfo = {.svid = svid,
|
||||
.constellation = type,
|
||||
.cN0Dbhz = cN0DbHz,
|
||||
.elevationDegrees = elevationDegrees,
|
||||
.azimuthDegrees = azimuthDegrees,
|
||||
.carrierFrequencyHz = carrierFrequencyHz,
|
||||
.svFlag = GnssSvFlags::USED_IN_FIX | GnssSvFlags::HAS_EPHEMERIS_DATA |
|
||||
GnssSvFlags::HAS_ALMANAC_DATA};
|
||||
GnssSvFlags::HAS_ALMANAC_DATA |
|
||||
GnssSvFlags::HAS_CARRIER_FREQUENCY};
|
||||
return svInfo;
|
||||
}
|
||||
|
||||
hidl_vec<GnssAntennaInfo> Utils::getMockAntennaInfos() {
|
||||
GnssAntennaInfo mockAntennaInfo_1 = {
|
||||
.carrierFrequencyMHz = 123412.12,
|
||||
.carrierFrequencyMHz = kGpsL1FreqHz * 1e-6,
|
||||
.phaseCenterOffsetCoordinateMillimeters = Coord{.x = 1,
|
||||
.xUncertainty = 0.1,
|
||||
.y = 2,
|
||||
@@ -381,7 +383,7 @@ hidl_vec<GnssAntennaInfo> Utils::getMockAntennaInfos() {
|
||||
};
|
||||
|
||||
GnssAntennaInfo mockAntennaInfo_2 = {
|
||||
.carrierFrequencyMHz = 532324.23,
|
||||
.carrierFrequencyMHz = kGpsL5FreqHz * 1e-6,
|
||||
.phaseCenterOffsetCoordinateMillimeters = Coord{.x = 5,
|
||||
.xUncertainty = 0.1,
|
||||
.y = 6,
|
||||
|
||||
@@ -29,6 +29,10 @@ const float kMockVerticalAccuracyMeters = 5;
|
||||
const float kMockSpeedAccuracyMetersPerSecond = 1;
|
||||
const float kMockBearingAccuracyDegrees = 90;
|
||||
const int64_t kMockTimestamp = 1519930775453L;
|
||||
const float kGpsL1FreqHz = 1575.42 * 1e6;
|
||||
const float kGpsL5FreqHz = 1176.45 * 1e6;
|
||||
const float kGloG1FreqHz = 1602.0 * 1e6;
|
||||
const float kIrnssL5FreqHz = 1176.45 * 1e6;
|
||||
|
||||
} // namespace common
|
||||
} // namespace gnss
|
||||
|
||||
@@ -44,7 +44,8 @@ struct Utils {
|
||||
static V1_0::IGnssCallback::GnssSvInfo getMockSvInfoV1_0(int16_t svid,
|
||||
V1_0::GnssConstellationType type,
|
||||
float cN0DbHz, float elevationDegrees,
|
||||
float azimuthDegrees);
|
||||
float azimuthDegrees,
|
||||
float carrierFrequencyHz);
|
||||
static hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo> getMockAntennaInfos();
|
||||
};
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ struct GnssTemplate : public T_IGnss {
|
||||
void reportLocation(const V2_0::GnssLocation&) const;
|
||||
void reportLocation(const V1_0::GnssLocation&) const;
|
||||
void reportSvStatus(const hidl_vec<V2_1::IGnssCallback::GnssSvInfo>&) const;
|
||||
void reportGnssStatusValue(const V1_0::IGnssCallback::GnssStatusValue) const;
|
||||
|
||||
Return<void> help(const hidl_handle& fd);
|
||||
Return<void> setLocation(const hidl_handle& fd, const hidl_vec<hidl_string>& options);
|
||||
@@ -215,6 +216,7 @@ Return<bool> GnssTemplate<T_IGnss>::start() {
|
||||
}
|
||||
|
||||
mIsActive = true;
|
||||
this->reportGnssStatusValue(V1_0::IGnssCallback::GnssStatusValue::SESSION_BEGIN);
|
||||
mThread = std::thread([this]() {
|
||||
while (mIsActive == true) {
|
||||
auto svStatus = filterBlocklistedSatellitesV2_1(Utils::getMockSvInfoListV2_1());
|
||||
@@ -266,6 +268,7 @@ template <class T_IGnss>
|
||||
Return<bool> GnssTemplate<T_IGnss>::stop() {
|
||||
ALOGD("stop");
|
||||
mIsActive = false;
|
||||
this->reportGnssStatusValue(V1_0::IGnssCallback::GnssStatusValue::SESSION_END);
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
@@ -605,6 +608,20 @@ Return<sp<V2_1::IGnssAntennaInfo>> GnssTemplate<T_IGnss>::getExtensionGnssAntenn
|
||||
return new V2_1::implementation::GnssAntennaInfo();
|
||||
}
|
||||
|
||||
template <class T_IGnss>
|
||||
void GnssTemplate<T_IGnss>::reportGnssStatusValue(
|
||||
const V1_0::IGnssCallback::GnssStatusValue gnssStatusValue) const {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (sGnssCallback_2_1 == nullptr) {
|
||||
ALOGE("%s: sGnssCallback v2.1 is null.", __func__);
|
||||
return;
|
||||
}
|
||||
auto ret = sGnssCallback_2_1->gnssStatusCb(gnssStatusValue);
|
||||
if (!ret.isOk()) {
|
||||
ALOGE("%s: Unable to invoke callback", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T_IGnss>
|
||||
void GnssTemplate<T_IGnss>::reportSvStatus(
|
||||
const hidl_vec<V2_1::IGnssCallback::GnssSvInfo>& svInfoList) const {
|
||||
|
||||
@@ -458,6 +458,7 @@ TEST_P(GraphicsCompositionTest, ClientComposition) {
|
||||
<< " pixel format: PixelFormat::RGBA_8888 dataspace: "
|
||||
<< ReadbackHelper::getDataspaceString(clientDataspace)
|
||||
<< " unsupported for display" << std::endl;
|
||||
mReader->mCompositionChanges.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define LOG_TAG "graphics_composer_hidl_hal_test@2.3"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
@@ -155,16 +156,31 @@ class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
|
||||
TEST_P(GraphicsComposerHidlTest, GetDisplayIdentificationData) {
|
||||
uint8_t port0;
|
||||
std::vector<uint8_t> data0;
|
||||
if (mComposerClient->getDisplayIdentificationData(mPrimaryDisplay, &port0, &data0)) {
|
||||
uint8_t port1;
|
||||
std::vector<uint8_t> data1;
|
||||
ASSERT_TRUE(mComposerClient->getDisplayIdentificationData(mPrimaryDisplay, &port1, &data1));
|
||||
|
||||
ASSERT_EQ(port0, port1) << "ports are not stable";
|
||||
ASSERT_TRUE(data0.size() == data1.size() &&
|
||||
std::equal(data0.begin(), data0.end(), data1.begin()))
|
||||
<< "data is not stable";
|
||||
if (!mComposerClient->getDisplayIdentificationData(mPrimaryDisplay, &port0, &data0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_FALSE(data0.empty());
|
||||
constexpr size_t kEdidBlockSize = 128;
|
||||
ASSERT_TRUE(data0.size() % kEdidBlockSize == 0)
|
||||
<< "EDID blob length is not a multiple of " << kEdidBlockSize;
|
||||
|
||||
const uint8_t kEdidHeader[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
ASSERT_TRUE(std::equal(std::begin(kEdidHeader), std::end(kEdidHeader), data0.begin()))
|
||||
<< "EDID blob doesn't start with the fixed EDID header";
|
||||
ASSERT_EQ(0, std::accumulate(data0.begin(), data0.begin() + kEdidBlockSize,
|
||||
static_cast<uint8_t>(0)))
|
||||
<< "EDID base block doesn't checksum";
|
||||
|
||||
uint8_t port1;
|
||||
std::vector<uint8_t> data1;
|
||||
ASSERT_TRUE(mComposerClient->getDisplayIdentificationData(mPrimaryDisplay, &port1, &data1));
|
||||
|
||||
ASSERT_EQ(port0, port1) << "ports are not stable";
|
||||
ASSERT_TRUE(data0.size() == data1.size() &&
|
||||
std::equal(data0.begin(), data0.end(), data1.begin()))
|
||||
<< "data is not stable";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="module-name" value="VtsHalGraphicsComposerV2_4TargetTest" />
|
||||
<option name="native-test-timeout" value="300000"/>
|
||||
<option name="native-test-timeout" value="900000"/>
|
||||
</test>
|
||||
</configuration>
|
||||
|
||||
@@ -438,7 +438,7 @@ bool verify_attestation_record(const string& challenge, const string& app_id,
|
||||
// TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
|
||||
// keymaster implementation will report YYYYMM dates instead of YYYYMMDD
|
||||
// for the BOOT_PATCH_LEVEL.
|
||||
if (avb_verification_enabled()) {
|
||||
if (!is_gsi()) {
|
||||
for (int i = 0; i < att_hw_enforced.size(); i++) {
|
||||
if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
|
||||
att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
|
||||
|
||||
@@ -31,21 +31,36 @@ import android.hardware.memtrack.MemtrackType;
|
||||
* accounting for stride, bit depth, rounding up to page size, etc.
|
||||
*
|
||||
* The following getMemory() categories are important for memory accounting in
|
||||
* `dumpsys meminfo` and should be reported as described below:
|
||||
* Android frameworks (e.g. `dumpsys meminfo`) and should be reported as described
|
||||
* below:
|
||||
*
|
||||
* - MemtrackType::GRAPHICS and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
|
||||
* This should report the PSS of all DMA buffers mapped by the process
|
||||
* with the specified PID. This PSS can be calculated using ReadDmaBufPss()
|
||||
* form libdmabufinfo.
|
||||
* This should report the PSS of all CPU-Mapped DMA-BUFs (buffers mapped into
|
||||
* the process address space) and all GPU-Mapped DMA-BUFs (buffers mapped into
|
||||
* the GPU device address space on behalf of the process), removing any overlap
|
||||
* between the CPU-mapped and GPU-mapped sets.
|
||||
*
|
||||
* - MemtrackType::GL and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
|
||||
* This category should report all GPU private allocations for the specified
|
||||
* PID that are not accounted in /proc/<pid>/smaps.
|
||||
*
|
||||
* getMemory() called with PID 0 should report the global total GPU-private
|
||||
* memory, for MemtrackType::GL and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
|
||||
*
|
||||
* getMemory() called with PID 0 for a MemtrackType other than GL should
|
||||
* report 0.
|
||||
*
|
||||
* - MemtrackType::OTHER and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
|
||||
* Any other memory not accounted for in /proc/<pid>/smaps if any, otherwise
|
||||
* this should return 0.
|
||||
*
|
||||
* SMAPS_UNACCOUNTED memory should also include memory that is mapped with
|
||||
* VM_PFNMAP flag set. For these mappings PSS and RSS are reported as 0 in smaps.
|
||||
* Such mappings have no backing page structs from which PSS/RSS can be calculated.
|
||||
*
|
||||
* Any memtrack operation that is not supported should return a binder status with
|
||||
* exception code EX_UNSUPPORTED_OPERATION.
|
||||
*
|
||||
* Constructor for the interface should be used to perform memtrack management
|
||||
* setup actions and must be called once before any calls to getMemory().
|
||||
*/
|
||||
|
||||
@@ -119,7 +119,7 @@ nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom
|
||||
<< numberOfCacheFiles.numDataCache << " vs " << nn::kMaxNumberOfCacheFiles
|
||||
<< ")";
|
||||
}
|
||||
return std::make_pair(numberOfCacheFiles.numDataCache, numberOfCacheFiles.numModelCache);
|
||||
return std::make_pair(numberOfCacheFiles.numModelCache, numberOfCacheFiles.numDataCache);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -58,7 +58,7 @@ const std::string kInvalidName = "";
|
||||
const std::shared_ptr<BnDevice> kInvalidDevice;
|
||||
constexpr PerformanceInfo kNoPerformanceInfo = {.execTime = std::numeric_limits<float>::max(),
|
||||
.powerUsage = std::numeric_limits<float>::max()};
|
||||
constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles,
|
||||
constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles - 1,
|
||||
.numDataCache = nn::kMaxNumberOfCacheFiles};
|
||||
|
||||
constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
|
||||
@@ -300,6 +300,21 @@ TEST(DeviceTest, getSupportedExtensionsDeadObject) {
|
||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||
}
|
||||
|
||||
TEST(DeviceTest, getNumberOfCacheFilesNeeded) {
|
||||
// setup call
|
||||
const auto mockDevice = createMockDevice();
|
||||
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
|
||||
|
||||
// run test
|
||||
const auto result = Device::create(kName, mockDevice);
|
||||
|
||||
// verify result
|
||||
ASSERT_TRUE(result.has_value());
|
||||
constexpr auto kNumberOfCacheFilesPair = std::make_pair<uint32_t, uint32_t>(
|
||||
kNumberOfCacheFiles.numModelCache, kNumberOfCacheFiles.numDataCache);
|
||||
EXPECT_EQ(result.value()->getNumberOfCacheFilesNeeded(), kNumberOfCacheFilesPair);
|
||||
}
|
||||
|
||||
TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
|
||||
// setup call
|
||||
const auto mockDevice = createMockDevice();
|
||||
|
||||
@@ -1251,8 +1251,20 @@ TEST_P(RadioHidlTest_v1_5, sendCdmaSmsExpectMore) {
|
||||
* Test IRadio.getBarringInfo() for the response returned.
|
||||
*/
|
||||
TEST_P(RadioHidlTest_v1_5, getBarringInfo) {
|
||||
// If the previous setRadioPower_1_5_emergencyCall_cancelled test has just finished.
|
||||
// Due to radio restarting, modem may need a little more time to acquire network service
|
||||
// and barring infos. If voice status is in-service, waiting 3s to get barring infos ready.
|
||||
// Or waiting 10s if voice status is not in-service.
|
||||
serial = GetRandomSerialNumber();
|
||||
radio_v1_5->getVoiceRegistrationState_1_5(serial);
|
||||
EXPECT_EQ(std::cv_status::no_timeout, wait());
|
||||
if (isVoiceInService(radioRsp_v1_5->voiceRegResp.regState)) {
|
||||
sleep(BARRING_INFO_MAX_WAIT_TIME_SECONDS);
|
||||
} else {
|
||||
sleep(VOICE_SERVICE_MAX_WAIT_TIME_SECONDS);
|
||||
}
|
||||
|
||||
serial = GetRandomSerialNumber();
|
||||
Return<void> res = radio_v1_5->getBarringInfo(serial);
|
||||
EXPECT_EQ(std::cv_status::no_timeout, wait());
|
||||
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
|
||||
|
||||
@@ -51,6 +51,8 @@ using ::android::hardware::Void;
|
||||
#define TIMEOUT_PERIOD 75
|
||||
#define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
|
||||
#define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
|
||||
#define VOICE_SERVICE_MAX_WAIT_TIME_SECONDS 10
|
||||
#define BARRING_INFO_MAX_WAIT_TIME_SECONDS 3
|
||||
|
||||
#define RADIO_SERVICE_NAME "slot1"
|
||||
|
||||
@@ -69,6 +71,7 @@ class RadioResponse_v1_5 : public ::android::hardware::radio::V1_5::IRadioRespon
|
||||
|
||||
// Call
|
||||
hidl_vec<::android::hardware::radio::V1_2::Call> currentCalls;
|
||||
::android::hardware::radio::V1_2::VoiceRegStateResult voiceRegResp;
|
||||
|
||||
// Modem
|
||||
bool isModemEnabled;
|
||||
|
||||
@@ -763,8 +763,9 @@ Return<void> RadioResponse_v1_5::getCellInfoListResponse_1_2(
|
||||
|
||||
Return<void> RadioResponse_v1_5::getVoiceRegistrationStateResponse_1_2(
|
||||
const RadioResponseInfo& info,
|
||||
const ::android::hardware::radio::V1_2::VoiceRegStateResult& /*voiceRegResponse*/) {
|
||||
const ::android::hardware::radio::V1_2::VoiceRegStateResult& voiceRegResponse) {
|
||||
rspInfo = info;
|
||||
voiceRegResp = voiceRegResponse;
|
||||
parent_v1_5.notify(info.serial);
|
||||
return Void();
|
||||
}
|
||||
@@ -989,8 +990,9 @@ Return<void> RadioResponse_v1_5::getBarringInfoResponse(
|
||||
|
||||
Return<void> RadioResponse_v1_5::getVoiceRegistrationStateResponse_1_5(
|
||||
const RadioResponseInfo& info,
|
||||
const ::android::hardware::radio::V1_5::RegStateResult& /*regResponse*/) {
|
||||
const ::android::hardware::radio::V1_5::RegStateResult& regResponse) {
|
||||
rspInfo = info;
|
||||
voiceRegResp.regState = regResponse.regState;
|
||||
parent_v1_5.notify(info.serial);
|
||||
return Void();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ parcelable DeviceInfo {
|
||||
* ? "vendor_patch_level" : uint, // YYYYMMDD
|
||||
* "version" : 1, // The CDDL schema version.
|
||||
* "security_level" : "tee" / "strongbox"
|
||||
* "att_id_state": "locked" / "open", // Attestation IDs State. If "locked", this
|
||||
* // indicates a device's attestable IDs are
|
||||
* // factory-locked and immutable. If "open",
|
||||
* // this indicates the device is still in a
|
||||
* // provisionable state and the attestable IDs
|
||||
* // are not yet frozen.
|
||||
* }
|
||||
*/
|
||||
byte[] deviceInfo;
|
||||
|
||||
@@ -277,6 +277,10 @@ interface IKeyMintDevice {
|
||||
* must return ErrorCode::INVALID_ARGUMENT. The values 3 and 65537 must be supported. It is
|
||||
* recommended to support all prime values up to 2^64.
|
||||
*
|
||||
* o Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER specify the valid date range for
|
||||
* the returned X.509 certificate holding the public key. If omitted, generateKey must return
|
||||
* ErrorCode::MISSING_NOT_BEFORE or ErrorCode::MISSING_NOT_AFTER.
|
||||
*
|
||||
* The following parameters are not necessary to generate a usable RSA key, but generateKey must
|
||||
* not return an error if they are omitted:
|
||||
*
|
||||
@@ -297,6 +301,10 @@ interface IKeyMintDevice {
|
||||
* Tag::EC_CURVE must be provided to generate an ECDSA key. If it is not provided, generateKey
|
||||
* must return ErrorCode::UNSUPPORTED_KEY_SIZE. TEE IKeyMintDevice implementations must support
|
||||
* all curves. StrongBox implementations must support P_256.
|
||||
|
||||
* Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER must be provided to specify the
|
||||
* valid date range for the returned X.509 certificate holding the public key. If omitted,
|
||||
* generateKey must return ErrorCode::MISSING_NOT_BEFORE or ErrorCode::MISSING_NOT_AFTER.
|
||||
*
|
||||
* == AES Keys ==
|
||||
*
|
||||
@@ -805,9 +813,10 @@ interface IKeyMintDevice {
|
||||
byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
|
||||
|
||||
/**
|
||||
* Returns parameters associated with the provided key. This should match the
|
||||
* KeyCharacteristics present in the KeyCreationResult returned by generateKey(),
|
||||
* importKey(), or importWrappedKey().
|
||||
* Returns KeyMint-enforced parameters associated with the provided key. The returned tags are
|
||||
* a subset of KeyCharacteristics found in the KeyCreationResult returned by generateKey(),
|
||||
* importKey(), or importWrappedKey(). The returned value is a subset, as it does not include
|
||||
* any Keystore-enforced parameters.
|
||||
*
|
||||
* @param keyBlob The opaque descriptor returned by generateKey, importKey or importWrappedKey.
|
||||
*
|
||||
|
||||
@@ -158,20 +158,7 @@ parcelable ProtectedData {
|
||||
* payload: bstr .cbor BccPayload
|
||||
* ]
|
||||
*
|
||||
* VerifiedDeviceInfo = {
|
||||
* ? "brand" : tstr,
|
||||
* ? "manufacturer" : tstr,
|
||||
* ? "product" : tstr,
|
||||
* ? "model" : tstr,
|
||||
* ? "board" : tstr,
|
||||
* ? "device" : tstr,
|
||||
* ? "vb_state" : "green" / "yellow" / "orange",
|
||||
* ? "bootloader_state" : "locked" / "unlocked",
|
||||
* ? "os_version" : tstr,
|
||||
* ? "system_patch_level" : uint, // YYYYMMDD
|
||||
* ? "boot_patch_level" : uint, // YYYYMMDD
|
||||
* ? "vendor_patch_level" : uint, // YYYYMMDD
|
||||
* }
|
||||
* VerifiedDeviceInfo = DeviceInfo // See DeviceInfo.aidl
|
||||
*
|
||||
* PubKeyX25519 = { // COSE_Key
|
||||
* 1 : 1, // Key type : Octet Key Pair
|
||||
|
||||
@@ -18,10 +18,6 @@ package android.hardware.security.keymint;
|
||||
|
||||
import android.hardware.security.keymint.TagType;
|
||||
|
||||
// TODO(seleneh) : note aidl currently does not support double nested enum definitions such as
|
||||
// ROOT_OF_TRUST = TagType:BYTES | 704. So we are forced to write definitions as
|
||||
// ROOT_OF_TRUST = (9 << 28) for now. Will need to flip this back later when aidl support is added.
|
||||
|
||||
/**
|
||||
* Tag specifies various kinds of tags that can be set in KeyParameter to identify what kind of
|
||||
* data are stored in KeyParameter.
|
||||
@@ -33,7 +29,7 @@ enum Tag {
|
||||
/**
|
||||
* Tag::INVALID should never be set. It means you hit an error.
|
||||
*/
|
||||
INVALID = (0 << 28) | 0,
|
||||
INVALID = 0,
|
||||
|
||||
/**
|
||||
* Tag::PURPOSE specifies the set of purposes for which the key may be used. Possible values
|
||||
@@ -47,7 +43,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
PURPOSE = (2 << 28) /* TagType:ENUM_REP */ | 1,
|
||||
PURPOSE = TagType.ENUM_REP | 1,
|
||||
|
||||
/**
|
||||
* Tag::ALGORITHM specifies the cryptographic algorithm with which the key is used. This tag
|
||||
@@ -56,7 +52,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
ALGORITHM = (1 << 28) /* TagType:ENUM */ | 2,
|
||||
ALGORITHM = TagType.ENUM | 2,
|
||||
|
||||
/**
|
||||
* Tag::KEY_SIZE specifies the size, in bits, of the key, measuring in the normal way for the
|
||||
@@ -68,7 +64,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
KEY_SIZE = (3 << 28) /* TagType:UINT */ | 3,
|
||||
KEY_SIZE = TagType.UINT | 3,
|
||||
|
||||
/**
|
||||
* Tag::BLOCK_MODE specifies the block cipher mode(s) with which the key may be used. This tag
|
||||
@@ -81,7 +77,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
BLOCK_MODE = (2 << 28) /* TagType:ENUM_REP */ | 4,
|
||||
BLOCK_MODE = TagType.ENUM_REP | 4,
|
||||
|
||||
/**
|
||||
* Tag::DIGEST specifies the digest algorithms that may be used with the key to perform signing
|
||||
@@ -95,7 +91,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 5,
|
||||
DIGEST = TagType.ENUM_REP | 5,
|
||||
|
||||
/**
|
||||
* Tag::PADDING specifies the padding modes that may be used with the key. This tag is relevant
|
||||
@@ -123,7 +119,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
PADDING = (2 << 28) /* TagType:ENUM_REP */ | 6,
|
||||
PADDING = TagType.ENUM_REP | 6,
|
||||
|
||||
/**
|
||||
* Tag::CALLER_NONCE specifies that the caller can provide a nonce for nonce-requiring
|
||||
@@ -136,7 +132,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
CALLER_NONCE = (7 << 28) /* TagType:BOOL */ | 7,
|
||||
CALLER_NONCE = TagType.BOOL | 7,
|
||||
|
||||
/**
|
||||
* Tag::MIN_MAC_LENGTH specifies the minimum length of MAC that can be requested or verified
|
||||
@@ -149,7 +145,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
MIN_MAC_LENGTH = (3 << 28) /* TagType:UINT */ | 8,
|
||||
MIN_MAC_LENGTH = TagType.UINT | 8,
|
||||
|
||||
// Tag 9 reserved
|
||||
|
||||
@@ -159,7 +155,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
EC_CURVE = (1 << 28) /* TagType:ENUM */ | 10,
|
||||
EC_CURVE = TagType.ENUM | 10,
|
||||
|
||||
/**
|
||||
* Tag::RSA_PUBLIC_EXPONENT specifies the value of the public exponent for an RSA key pair.
|
||||
@@ -173,7 +169,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
RSA_PUBLIC_EXPONENT = (5 << 28) /* TagType:ULONG */ | 200,
|
||||
RSA_PUBLIC_EXPONENT = TagType.ULONG | 200,
|
||||
|
||||
// Tag 201 reserved
|
||||
|
||||
@@ -184,7 +180,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
INCLUDE_UNIQUE_ID = (7 << 28) /* TagType:BOOL */ | 202,
|
||||
INCLUDE_UNIQUE_ID = TagType.BOOL | 202,
|
||||
|
||||
/**
|
||||
* Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with RSA
|
||||
@@ -197,7 +193,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
|
||||
RSA_OAEP_MGF_DIGEST = TagType.ENUM_REP | 203,
|
||||
|
||||
// Tag 301 reserved
|
||||
|
||||
@@ -209,7 +205,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
BOOTLOADER_ONLY = (7 << 28) /* TagType:BOOL */ | 302,
|
||||
BOOTLOADER_ONLY = TagType.BOOL | 302,
|
||||
|
||||
/**
|
||||
* Tag::ROLLBACK_RESISTANCE specifies that the key has rollback resistance, meaning that when
|
||||
@@ -224,10 +220,10 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
ROLLBACK_RESISTANCE = (7 << 28) /* TagType:BOOL */ | 303,
|
||||
ROLLBACK_RESISTANCE = TagType.BOOL | 303,
|
||||
|
||||
// Reserved for future use.
|
||||
HARDWARE_TYPE = (1 << 28) /* TagType:ENUM */ | 304,
|
||||
HARDWARE_TYPE = TagType.ENUM | 304,
|
||||
|
||||
/**
|
||||
* Keys tagged with EARLY_BOOT_ONLY may only be used during early boot, until
|
||||
@@ -236,7 +232,7 @@ enum Tag {
|
||||
* provided to IKeyMintDevice::importKey, the import must fail with
|
||||
* ErrorCode::EARLY_BOOT_ENDED.
|
||||
*/
|
||||
EARLY_BOOT_ONLY = (7 << 28) /* TagType:BOOL */ | 305,
|
||||
EARLY_BOOT_ONLY = TagType.BOOL | 305,
|
||||
|
||||
/**
|
||||
* Tag::ACTIVE_DATETIME specifies the date and time at which the key becomes active, in
|
||||
@@ -245,7 +241,7 @@ enum Tag {
|
||||
*
|
||||
* Need not be hardware-enforced.
|
||||
*/
|
||||
ACTIVE_DATETIME = (6 << 28) /* TagType:DATE */ | 400,
|
||||
ACTIVE_DATETIME = TagType.DATE | 400,
|
||||
|
||||
/**
|
||||
* Tag::ORIGINATION_EXPIRE_DATETIME specifies the date and time at which the key expires for
|
||||
@@ -257,7 +253,7 @@ enum Tag {
|
||||
*
|
||||
* Need not be hardware-enforced.
|
||||
*/
|
||||
ORIGINATION_EXPIRE_DATETIME = (6 << 28) /* TagType:DATE */ | 401,
|
||||
ORIGINATION_EXPIRE_DATETIME = TagType.DATE | 401,
|
||||
|
||||
/**
|
||||
* Tag::USAGE_EXPIRE_DATETIME specifies the date and time at which the key expires for
|
||||
@@ -269,7 +265,7 @@ enum Tag {
|
||||
*
|
||||
* Need not be hardware-enforced.
|
||||
*/
|
||||
USAGE_EXPIRE_DATETIME = (6 << 28) /* TagType:DATE */ | 402,
|
||||
USAGE_EXPIRE_DATETIME = TagType.DATE | 402,
|
||||
|
||||
/**
|
||||
* TODO(seleneh) this tag need to be deleted.
|
||||
@@ -293,8 +289,10 @@ enum Tag {
|
||||
* fails because the table is full, KeyMint returns ErrorCode::TOO_MANY_OPERATIONS.
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*
|
||||
* TODO(b/191738660): Remove in KeyMint V2. Currently only used for FDE.
|
||||
*/
|
||||
MIN_SECONDS_BETWEEN_OPS = (3 << 28) /* TagType:UINT */ | 403,
|
||||
MIN_SECONDS_BETWEEN_OPS = TagType.UINT | 403,
|
||||
|
||||
/**
|
||||
* Tag::MAX_USES_PER_BOOT specifies the maximum number of times that a key may be used between
|
||||
@@ -314,7 +312,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
MAX_USES_PER_BOOT = (3 << 28) /* TagType:UINT */ | 404,
|
||||
MAX_USES_PER_BOOT = TagType.UINT | 404,
|
||||
|
||||
/**
|
||||
* Tag::USAGE_COUNT_LIMIT specifies the number of times that a key may be used. This can be
|
||||
@@ -343,14 +341,14 @@ enum Tag {
|
||||
* record. This tag must have the same SecurityLevel as the tag that is added to the key
|
||||
* characteristics.
|
||||
*/
|
||||
USAGE_COUNT_LIMIT = (3 << 28) | 405, /* TagType:UINT */
|
||||
USAGE_COUNT_LIMIT = TagType.UINT | 405,
|
||||
|
||||
/**
|
||||
* Tag::USER_ID specifies the ID of the Android user that is permitted to use the key.
|
||||
*
|
||||
* Must not be hardware-enforced.
|
||||
*/
|
||||
USER_ID = (3 << 28) /* TagType:UINT */ | 501,
|
||||
USER_ID = TagType.UINT | 501,
|
||||
|
||||
/**
|
||||
* Tag::USER_SECURE_ID specifies that a key may only be used under a particular secure user
|
||||
@@ -383,7 +381,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
USER_SECURE_ID = (10 << 28) /* TagType:ULONG_REP */ | 502,
|
||||
USER_SECURE_ID = TagType.ULONG_REP | 502,
|
||||
|
||||
/**
|
||||
* Tag::NO_AUTH_REQUIRED specifies that no authentication is required to use this key. This tag
|
||||
@@ -391,7 +389,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
NO_AUTH_REQUIRED = (7 << 28) /* TagType:BOOL */ | 503,
|
||||
NO_AUTH_REQUIRED = TagType.BOOL | 503,
|
||||
|
||||
/**
|
||||
* Tag::USER_AUTH_TYPE specifies the types of user authenticators that may be used to authorize
|
||||
@@ -410,7 +408,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
USER_AUTH_TYPE = (1 << 28) /* TagType:ENUM */ | 504,
|
||||
USER_AUTH_TYPE = TagType.ENUM | 504,
|
||||
|
||||
/**
|
||||
* Tag::AUTH_TIMEOUT specifies the time in seconds for which the key is authorized for use,
|
||||
@@ -424,7 +422,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
AUTH_TIMEOUT = (3 << 28) /* TagType:UINT */ | 505,
|
||||
AUTH_TIMEOUT = TagType.UINT | 505,
|
||||
|
||||
/**
|
||||
* Tag::ALLOW_WHILE_ON_BODY specifies that the key may be used after authentication timeout if
|
||||
@@ -432,7 +430,7 @@ enum Tag {
|
||||
*
|
||||
* Cannot be hardware-enforced.
|
||||
*/
|
||||
ALLOW_WHILE_ON_BODY = (7 << 28) /* TagType:BOOL */ | 506,
|
||||
ALLOW_WHILE_ON_BODY = TagType.BOOL | 506,
|
||||
|
||||
/**
|
||||
* TRUSTED_USER_PRESENCE_REQUIRED is an optional feature that specifies that this key must be
|
||||
@@ -479,29 +477,31 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
TRUSTED_USER_PRESENCE_REQUIRED = (7 << 28) /* TagType:BOOL */ | 507,
|
||||
TRUSTED_USER_PRESENCE_REQUIRED = TagType.BOOL | 507,
|
||||
|
||||
/**
|
||||
* Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
|
||||
* specifies that this key must not be usable unless the user provides confirmation of the data
|
||||
* to be signed. Confirmation is proven to keyMint via an approval token. See
|
||||
* CONFIRMATION_TOKEN, as well as the ConfirmationUI HAL.
|
||||
* specifies that this key must not be usable unless the user provides confirmation of the data
|
||||
* to be signed. Confirmation is proven to keyMint via an approval token. See the authToken
|
||||
* parameter of begin(), as well as the ConfirmationUI HAL.
|
||||
*
|
||||
* If an attempt to use a key with this tag does not have a cryptographically valid
|
||||
* CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
|
||||
* token provided to finish() or if the data provided to update()/finish() does not
|
||||
* match the data described in the token, keyMint must return NO_USER_CONFIRMATION.
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
TRUSTED_CONFIRMATION_REQUIRED = (7 << 28) /* TagType:BOOL */ | 508,
|
||||
TRUSTED_CONFIRMATION_REQUIRED = TagType.BOOL | 508,
|
||||
|
||||
/**
|
||||
* Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
|
||||
* unlocked.
|
||||
* unlocked, as reported to KeyMint via authToken operation parameter and the
|
||||
* IKeyMintDevice::deviceLocked() method
|
||||
*
|
||||
* Must be software-enforced.
|
||||
* Must be hardware-enforced (but is also keystore-enforced on a per-user basis: see the
|
||||
* deviceLocked() documentation).
|
||||
*/
|
||||
UNLOCKED_DEVICE_REQUIRED = (7 << 28) /* TagType:BOOL */ | 509,
|
||||
UNLOCKED_DEVICE_REQUIRED = TagType.BOOL | 509,
|
||||
|
||||
/**
|
||||
* Tag::APPLICATION_ID. When provided to generateKey or importKey, this tag specifies data
|
||||
@@ -517,7 +517,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
APPLICATION_ID = (9 << 28) /* TagType:BYTES */ | 601,
|
||||
APPLICATION_ID = TagType.BYTES | 601,
|
||||
|
||||
/*
|
||||
* Semantically unenforceable tags, either because they have no specific meaning or because
|
||||
@@ -538,7 +538,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
APPLICATION_DATA = (9 << 28) /* TagType:BYTES */ | 700,
|
||||
APPLICATION_DATA = TagType.BYTES | 700,
|
||||
|
||||
/**
|
||||
* Tag::CREATION_DATETIME specifies the date and time the key was created, in milliseconds since
|
||||
@@ -546,7 +546,7 @@ enum Tag {
|
||||
*
|
||||
* Must be in the software-enforced list, if provided.
|
||||
*/
|
||||
CREATION_DATETIME = (6 << 28) /* TagType:DATE */ | 701,
|
||||
CREATION_DATETIME = TagType.DATE | 701,
|
||||
|
||||
/**
|
||||
* Tag::ORIGIN specifies where the key was created, if known. This tag must not be specified
|
||||
@@ -555,7 +555,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
ORIGIN = (1 << 28) /* TagType:ENUM */ | 702,
|
||||
ORIGIN = TagType.ENUM | 702,
|
||||
|
||||
// 703 is unused.
|
||||
|
||||
@@ -567,7 +567,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ROOT_OF_TRUST = (9 << 28) /* TagType:BYTES */ | 704,
|
||||
ROOT_OF_TRUST = TagType.BYTES | 704,
|
||||
|
||||
/**
|
||||
* Tag::OS_VERSION specifies the system OS version with which the key may be used. This tag is
|
||||
@@ -590,7 +590,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
OS_VERSION = (3 << 28) /* TagType:UINT */ | 705,
|
||||
OS_VERSION = TagType.UINT | 705,
|
||||
|
||||
/**
|
||||
* Tag::OS_PATCHLEVEL specifies the system security patch level with which the key may be used.
|
||||
@@ -611,7 +611,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
OS_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 706,
|
||||
OS_PATCHLEVEL = TagType.UINT | 706,
|
||||
|
||||
/**
|
||||
* Tag::UNIQUE_ID specifies a unique, time-based identifier. This tag is never provided to or
|
||||
@@ -646,7 +646,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
UNIQUE_ID = (9 << 28) /* TagType:BYTES */ | 707,
|
||||
UNIQUE_ID = TagType.BYTES | 707,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attested key
|
||||
@@ -655,7 +655,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_CHALLENGE = (9 << 28) /* TagType:BYTES */ | 708,
|
||||
ATTESTATION_CHALLENGE = TagType.BYTES | 708,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
|
||||
@@ -681,7 +681,7 @@ enum Tag {
|
||||
*
|
||||
* Cannot be hardware-enforced.
|
||||
*/
|
||||
ATTESTATION_APPLICATION_ID = (9 << 28) /* TagType:BYTES */ | 709,
|
||||
ATTESTATION_APPLICATION_ID = TagType.BYTES | 709,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
|
||||
@@ -694,7 +694,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_BRAND = (9 << 28) /* TagType:BYTES */ | 710,
|
||||
ATTESTATION_ID_BRAND = TagType.BYTES | 710,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
|
||||
@@ -707,7 +707,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_DEVICE = (9 << 28) /* TagType:BYTES */ | 711,
|
||||
ATTESTATION_ID_DEVICE = TagType.BYTES | 711,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
|
||||
@@ -720,7 +720,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_PRODUCT = (9 << 28) /* TagType:BYTES */ | 712,
|
||||
ATTESTATION_ID_PRODUCT = TagType.BYTES | 712,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_SERIAL the device's serial number. This field must be set only when
|
||||
@@ -732,7 +732,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_SERIAL = (9 << 28) /* TagType:BYTES */ | 713,
|
||||
ATTESTATION_ID_SERIAL = TagType.BYTES | 713,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attested key
|
||||
@@ -745,7 +745,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_IMEI = (9 << 28) /* TagType:BYTES */ | 714,
|
||||
ATTESTATION_ID_IMEI = TagType.BYTES | 714,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attested key
|
||||
@@ -758,7 +758,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_MEID = (9 << 28) /* TagType:BYTES */ | 715,
|
||||
ATTESTATION_ID_MEID = TagType.BYTES | 715,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
|
||||
@@ -771,7 +771,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_MANUFACTURER = (9 << 28) /* TagType:BYTES */ | 716,
|
||||
ATTESTATION_ID_MANUFACTURER = TagType.BYTES | 716,
|
||||
|
||||
/**
|
||||
* Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
|
||||
@@ -784,7 +784,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
ATTESTATION_ID_MODEL = (9 << 28) /* TagType:BYTES */ | 717,
|
||||
ATTESTATION_ID_MODEL = TagType.BYTES | 717,
|
||||
|
||||
/**
|
||||
* Tag::VENDOR_PATCHLEVEL specifies the vendor image security patch level with which the key may
|
||||
@@ -806,7 +806,7 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
VENDOR_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 718,
|
||||
VENDOR_PATCHLEVEL = TagType.UINT | 718,
|
||||
|
||||
/**
|
||||
* Tag::BOOT_PATCHLEVEL specifies the boot image (kernel) security patch level with which the
|
||||
@@ -826,16 +826,26 @@ enum Tag {
|
||||
*
|
||||
* Must be hardware-enforced.
|
||||
*/
|
||||
BOOT_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 719,
|
||||
BOOT_PATCHLEVEL = TagType.UINT | 719,
|
||||
|
||||
/**
|
||||
* DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attested key generation/import
|
||||
* operations. It indicates that attestation using a device-unique key is requested, rather
|
||||
* than a batch key. When a device-unique key is used, the returned chain should contain two
|
||||
* certificates:
|
||||
* than a batch key. When a device-unique key is used, the returned chain contains two or
|
||||
* three certificates.
|
||||
*
|
||||
* In case the chain contains two certificates, they should be:
|
||||
* * The attestation certificate, containing the attestation extension, as described in
|
||||
KeyCreationResult.aidl.
|
||||
* KeyCreationResult.aidl.
|
||||
* * A self-signed root certificate, signed by the device-unique key.
|
||||
*
|
||||
* In case the chain contains three certificates, they should be:
|
||||
* * The attestation certificate, containing the attestation extension, as described in
|
||||
* KeyCreationResult.aidl, signed by the device-unique key.
|
||||
* * An intermediate certificate, containing the public portion of the device-unique key.
|
||||
* * A self-signed root certificate, signed by a dedicated key, certifying the
|
||||
* intermediate.
|
||||
*
|
||||
* No additional chained certificates are provided. Only SecurityLevel::STRONGBOX
|
||||
* IKeyMintDevices may support device-unique attestations. SecurityLevel::TRUSTED_ENVIRONMENT
|
||||
* IKeyMintDevices must return ErrorCode::INVALID_ARGUMENT if they receive
|
||||
@@ -852,7 +862,7 @@ enum Tag {
|
||||
* IKeyMintDevice implementations that support device-unique attestation MUST add the
|
||||
* DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
|
||||
*/
|
||||
DEVICE_UNIQUE_ATTESTATION = (7 << 28) /* TagType:BOOL */ | 720,
|
||||
DEVICE_UNIQUE_ATTESTATION = TagType.BOOL | 720,
|
||||
|
||||
/**
|
||||
* IDENTITY_CREDENTIAL_KEY is never used by IKeyMintDevice, is not a valid argument to key
|
||||
@@ -860,7 +870,7 @@ enum Tag {
|
||||
* attestation. It is used in attestations produced by the IIdentityCredential HAL when that
|
||||
* HAL attests to Credential Keys. IIdentityCredential produces KeyMint-style attestations.
|
||||
*/
|
||||
IDENTITY_CREDENTIAL_KEY = (7 << 28) /* TagType:BOOL */ | 721,
|
||||
IDENTITY_CREDENTIAL_KEY = TagType.BOOL | 721,
|
||||
|
||||
/**
|
||||
* To prevent keys from being compromised if an attacker acquires read access to system / kernel
|
||||
@@ -870,19 +880,21 @@ enum Tag {
|
||||
*
|
||||
* STORAGE_KEY is used to denote that a key generated or imported is a key used for storage
|
||||
* encryption. Keys of this type can either be generated or imported or secure imported using
|
||||
* keyMint. exportKey() can be used to re-wrap storage key with a per-boot ephemeral key
|
||||
* wrapped key once the key characteristics are enforced.
|
||||
* keyMint. The convertStorageKeyToEphemeral() method of IKeyMintDevice can be used to re-wrap
|
||||
* storage key with a per-boot ephemeral key wrapped key once the key characteristics are
|
||||
* enforced.
|
||||
*
|
||||
* Keys with this tag cannot be used for any operation within keyMint.
|
||||
* ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to
|
||||
* begin().
|
||||
*/
|
||||
STORAGE_KEY = (7 << 28) /* TagType:BOOL */ | 722,
|
||||
STORAGE_KEY = TagType.BOOL | 722,
|
||||
|
||||
/**
|
||||
* TODO: Delete when keystore1 is deleted.
|
||||
* OBSOLETE: Do not use. See IKeyMintOperation.updateAad instead.
|
||||
* TODO(b/191738660): Remove in KeyMint v2.
|
||||
*/
|
||||
ASSOCIATED_DATA = (9 << 28) /* TagType:BYTES */ | 1000,
|
||||
ASSOCIATED_DATA = TagType.BYTES | 1000,
|
||||
|
||||
/**
|
||||
* Tag::NONCE is used to provide or return a nonce or Initialization Vector (IV) for AES-GCM,
|
||||
@@ -897,7 +909,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
NONCE = (9 << 28) /* TagType:BYTES */ | 1001,
|
||||
NONCE = TagType.BYTES | 1001,
|
||||
|
||||
/**
|
||||
* Tag::MAC_LENGTH provides the requested length of a MAC or GCM authentication tag, in bits.
|
||||
@@ -908,7 +920,7 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
MAC_LENGTH = (3 << 28) /* TagType:UINT */ | 1003,
|
||||
MAC_LENGTH = TagType.UINT | 1003,
|
||||
|
||||
/**
|
||||
* Tag::RESET_SINCE_ID_ROTATION specifies whether the device has been factory reset since the
|
||||
@@ -916,16 +928,15 @@ enum Tag {
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
*/
|
||||
RESET_SINCE_ID_ROTATION = (7 << 28) /* TagType:BOOL */ | 1004,
|
||||
RESET_SINCE_ID_ROTATION = TagType.BOOL | 1004,
|
||||
|
||||
/**
|
||||
* Tag::CONFIRMATION_TOKEN is used to deliver a cryptographic token proving that the user
|
||||
* confirmed a signing request. The content is a full-length HMAC-SHA256 value. See the
|
||||
* ConfirmationUI HAL for details of token computation.
|
||||
* OBSOLETE: Do not use. See the authToken parameter for IKeyMintDevice::begin and for
|
||||
* IKeyMintOperation methods instead.
|
||||
*
|
||||
* Must never appear in KeyCharacteristics.
|
||||
* TODO(b/191738660): Delete when keystore1 is deleted.
|
||||
*/
|
||||
CONFIRMATION_TOKEN = (9 << 28) /* TagType:BYTES */ | 1005,
|
||||
CONFIRMATION_TOKEN = TagType.BYTES | 1005,
|
||||
|
||||
/**
|
||||
* Tag::CERTIFICATE_SERIAL specifies the serial number to be assigned to the attestation
|
||||
@@ -933,7 +944,7 @@ enum Tag {
|
||||
* keyMint in the attestation parameters during generateKey() and importKey(). If not provided,
|
||||
* the serial shall default to 1.
|
||||
*/
|
||||
CERTIFICATE_SERIAL = (8 << 28) /* TagType:BIGNUM */ | 1006,
|
||||
CERTIFICATE_SERIAL = TagType.BIGNUM | 1006,
|
||||
|
||||
/**
|
||||
* Tag::CERTIFICATE_SUBJECT the certificate subject. The value is a DER encoded X509 NAME.
|
||||
@@ -941,7 +952,7 @@ enum Tag {
|
||||
* during generateKey and importKey. If not provided the subject name shall default to
|
||||
* CN="Android Keystore Key".
|
||||
*/
|
||||
CERTIFICATE_SUBJECT = (9 << 28) /* TagType:BYTES */ | 1007,
|
||||
CERTIFICATE_SUBJECT = TagType.BYTES | 1007,
|
||||
|
||||
/**
|
||||
* Tag::CERTIFICATE_NOT_BEFORE the beginning of the validity of the certificate in UNIX epoch
|
||||
@@ -949,7 +960,7 @@ enum Tag {
|
||||
* certificates. ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if
|
||||
* this tag is not provided to generateKey or importKey.
|
||||
*/
|
||||
CERTIFICATE_NOT_BEFORE = (6 << 28) /* TagType:DATE */ | 1008,
|
||||
CERTIFICATE_NOT_BEFORE = TagType.DATE | 1008,
|
||||
|
||||
/**
|
||||
* Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch time in
|
||||
@@ -957,7 +968,7 @@ enum Tag {
|
||||
* ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey or
|
||||
* importKey.
|
||||
*/
|
||||
CERTIFICATE_NOT_AFTER = (6 << 28) /* TagType:DATE */ | 1009,
|
||||
CERTIFICATE_NOT_AFTER = TagType.DATE | 1009,
|
||||
|
||||
/**
|
||||
* Tag::MAX_BOOT_LEVEL specifies a maximum boot level at which a key should function.
|
||||
@@ -968,5 +979,5 @@ enum Tag {
|
||||
*
|
||||
* Cannot be hardware enforced in this version.
|
||||
*/
|
||||
MAX_BOOT_LEVEL = (3 << 28) /* TagType:UINT */ | 1010,
|
||||
MAX_BOOT_LEVEL = TagType.UINT | 1010,
|
||||
}
|
||||
|
||||
@@ -23,16 +23,11 @@ package {
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "VtsAidlKeyMintTargetTest",
|
||||
cc_defaults {
|
||||
name: "keymint_vts_defaults",
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
],
|
||||
srcs: [
|
||||
"AttestKeyTest.cpp",
|
||||
"DeviceUniqueAttestationTest.cpp",
|
||||
"KeyMintTest.cpp",
|
||||
"VtsHalTargetTestDefaults",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder_ndk",
|
||||
@@ -43,9 +38,24 @@ cc_test {
|
||||
"android.hardware.security.secureclock-V1-ndk_platform",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libjsoncpp",
|
||||
"libkeymint",
|
||||
"libkeymint_remote_prov_support",
|
||||
"libkeymint_support",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "VtsAidlKeyMintTargetTest",
|
||||
defaults: [
|
||||
"keymint_vts_defaults",
|
||||
],
|
||||
srcs: [
|
||||
"AttestKeyTest.cpp",
|
||||
"DeviceUniqueAttestationTest.cpp",
|
||||
"KeyMintTest.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libkeymint_vts_test_utils",
|
||||
],
|
||||
test_suites: [
|
||||
@@ -57,8 +67,7 @@ cc_test {
|
||||
cc_test_library {
|
||||
name: "libkeymint_vts_test_utils",
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
"keymint_vts_defaults",
|
||||
],
|
||||
srcs: [
|
||||
"KeyMintAidlTestBase.cpp",
|
||||
@@ -66,48 +75,26 @@ cc_test_library {
|
||||
export_include_dirs: [
|
||||
".",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder_ndk",
|
||||
"libcrypto",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.security.keymint-V1-ndk_platform",
|
||||
"android.hardware.security.secureclock-V1-ndk_platform",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libgmock_ndk",
|
||||
"libkeymint",
|
||||
"libkeymint_remote_prov_support",
|
||||
"libkeymint_support",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "VtsHalRemotelyProvisionedComponentTargetTest",
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
"keymint_vts_defaults",
|
||||
],
|
||||
srcs: [
|
||||
"VtsRemotelyProvisionedComponentTests.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder_ndk",
|
||||
"libcrypto",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.security.keymint-V1-ndk_platform",
|
||||
"android.hardware.security.secureclock-V1-ndk_platform",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libgmock_ndk",
|
||||
"libkeymaster_portable",
|
||||
"libkeymint",
|
||||
"libkeymint_support",
|
||||
"libkeymint_remote_prov_support",
|
||||
"libkeymint_vts_test_utils",
|
||||
"libpuresoftkeymasterdevice",
|
||||
],
|
||||
test_config: "VtsRemotelyProvisionedComponentTests.xml",
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts",
|
||||
|
||||
@@ -40,11 +40,16 @@ class DeviceUniqueAttestationTest : public KeyMintAidlTestBase {
|
||||
|
||||
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
||||
|
||||
// The device-unique attestation chain should contain exactly two certificates:
|
||||
// The device-unique attestation chain should contain exactly three certificates:
|
||||
// * The leaf with the attestation extension.
|
||||
// * A self-signed root, signed using the device-unique key.
|
||||
ASSERT_EQ(cert_chain_.size(), 2);
|
||||
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
||||
// * An intermediate, signing the leaf using the device-unique key.
|
||||
// * A self-signed root, signed using some authority's key, certifying
|
||||
// the device-unique key.
|
||||
const size_t chain_length = cert_chain_.size();
|
||||
ASSERT_TRUE(chain_length == 2 || chain_length == 3);
|
||||
// TODO(b/191361618): Once StrongBox implementations use a correctly-issued
|
||||
// certificate chain, do not skip issuers matching.
|
||||
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_, /* strict_issuer_check= */ false));
|
||||
|
||||
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
||||
EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
|
||||
|
||||
@@ -1493,7 +1493,8 @@ AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_
|
||||
return authList;
|
||||
}
|
||||
|
||||
AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain) {
|
||||
AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
|
||||
bool strict_issuer_check) {
|
||||
std::stringstream cert_data;
|
||||
|
||||
for (size_t i = 0; i < chain.size(); ++i) {
|
||||
@@ -1520,7 +1521,7 @@ AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain) {
|
||||
|
||||
string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
|
||||
string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
|
||||
if (cert_issuer != signer_subj) {
|
||||
if (cert_issuer != signer_subj && strict_issuer_check) {
|
||||
return AssertionFailure() << "Cert " << i << " has wrong issuer.\n"
|
||||
<< " Signer subject is " << signer_subj
|
||||
<< " Issuer subject is " << cert_issuer << endl
|
||||
|
||||
@@ -349,7 +349,8 @@ void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
|
||||
|
||||
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
|
||||
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
|
||||
::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
|
||||
::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
|
||||
bool strict_issuer_check = true);
|
||||
|
||||
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
|
||||
INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
|
||||
|
||||
@@ -1487,9 +1487,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) {
|
||||
tag.tag == TAG_ROLLBACK_RESISTANCE) {
|
||||
continue;
|
||||
}
|
||||
if (result == ErrorCode::UNSUPPORTED_TAG &&
|
||||
(tag.tag == TAG_ALLOW_WHILE_ON_BODY || tag.tag == TAG_TRUSTED_USER_PRESENCE_REQUIRED)) {
|
||||
// Optional tag not supported by this KeyMint implementation.
|
||||
if (result == ErrorCode::UNSUPPORTED_TAG && tag.tag == TAG_TRUSTED_USER_PRESENCE_REQUIRED) {
|
||||
// Tag not required to be supported by all KeyMint implementations.
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(result, ErrorCode::OK);
|
||||
@@ -1501,9 +1500,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) {
|
||||
|
||||
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
||||
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
||||
if (tag.tag != TAG_ATTESTATION_APPLICATION_ID) {
|
||||
// Expect to find most of the extra tags in the key characteristics
|
||||
// of the generated key (but not for ATTESTATION_APPLICATION_ID).
|
||||
// Some tags are optional, so don't require them to be in the enforcements.
|
||||
if (tag.tag != TAG_ATTESTATION_APPLICATION_ID && tag.tag != TAG_ALLOW_WHILE_ON_BODY) {
|
||||
EXPECT_TRUE(hw_enforced.Contains(tag.tag) || sw_enforced.Contains(tag.tag))
|
||||
<< tag << " not in hw:" << hw_enforced << " nor sw:" << sw_enforced;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <openssl/ec_key.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <remote_prov/remote_prov_utils.h>
|
||||
#include <vector>
|
||||
|
||||
#include "KeyMintAidlTestBase.h"
|
||||
|
||||
@@ -40,6 +41,7 @@ using ::std::vector;
|
||||
namespace {
|
||||
|
||||
#define INSTANTIATE_REM_PROV_AIDL_TEST(name) \
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name); \
|
||||
INSTANTIATE_TEST_SUITE_P( \
|
||||
PerInstance, name, \
|
||||
testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \
|
||||
@@ -102,8 +104,8 @@ ErrMsgOr<cppbor::Array> corrupt_sig(const cppbor::Array* coseSign1) {
|
||||
return std::move(corruptSig);
|
||||
}
|
||||
|
||||
ErrMsgOr<EekChain> corrupt_sig_chain(const EekChain& eek, int which) {
|
||||
auto [chain, _, parseErr] = cppbor::parse(eek.chain);
|
||||
ErrMsgOr<bytevec> corrupt_sig_chain(const bytevec& encodedEekChain, int which) {
|
||||
auto [chain, _, parseErr] = cppbor::parse(encodedEekChain);
|
||||
if (!chain || !chain->asArray()) {
|
||||
return "EekChain parse failed";
|
||||
}
|
||||
@@ -125,7 +127,7 @@ ErrMsgOr<EekChain> corrupt_sig_chain(const EekChain& eek, int which) {
|
||||
corruptChain.add(eekChain->get(ii)->clone());
|
||||
}
|
||||
}
|
||||
return EekChain{corruptChain.encode(), eek.last_pubkey, eek.last_privkey};
|
||||
return corruptChain.encode();
|
||||
}
|
||||
|
||||
string device_suffix(const string& name) {
|
||||
@@ -271,14 +273,14 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) {
|
||||
class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
||||
protected:
|
||||
CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) {
|
||||
generateEek(3);
|
||||
generateTestEekChain(3);
|
||||
}
|
||||
|
||||
void generateEek(size_t eekLength) {
|
||||
void generateTestEekChain(size_t eekLength) {
|
||||
auto chain = generateEekChain(eekLength, eekId_);
|
||||
EXPECT_TRUE(chain) << chain.message();
|
||||
if (chain) eekChain_ = chain.moveValue();
|
||||
eekLength_ = eekLength;
|
||||
if (chain) testEekChain_ = chain.moveValue();
|
||||
testEekLength_ = eekLength;
|
||||
}
|
||||
|
||||
void generateKeys(bool testMode, size_t numKeys) {
|
||||
@@ -297,7 +299,8 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
||||
}
|
||||
|
||||
void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const bytevec& keysToSignMac, const ProtectedData& protectedData) {
|
||||
const bytevec& keysToSignMac, const ProtectedData& protectedData,
|
||||
std::vector<BccEntryData>* bccOutput = nullptr) {
|
||||
auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
|
||||
ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
|
||||
ASSERT_TRUE(parsedProtectedData->asArray());
|
||||
@@ -307,8 +310,9 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
||||
ASSERT_TRUE(senderPubkey) << senderPubkey.message();
|
||||
EXPECT_EQ(senderPubkey->second, eekId_);
|
||||
|
||||
auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
auto sessionKey =
|
||||
x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
ASSERT_TRUE(sessionKey) << sessionKey.message();
|
||||
|
||||
auto protectedDataPayload =
|
||||
@@ -354,11 +358,15 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
||||
|
||||
auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
|
||||
ASSERT_TRUE(macPayload) << macPayload.message();
|
||||
|
||||
if (bccOutput) {
|
||||
*bccOutput = std::move(*bccContents);
|
||||
}
|
||||
}
|
||||
|
||||
bytevec eekId_;
|
||||
size_t eekLength_;
|
||||
EekChain eekChain_;
|
||||
size_t testEekLength_;
|
||||
EekChain testEekChain_;
|
||||
bytevec challenge_;
|
||||
std::vector<MacedPublicKey> keysToSign_;
|
||||
cppbor::Array cborKeysToSign_;
|
||||
@@ -372,13 +380,13 @@ TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
|
||||
bool testMode = true;
|
||||
for (size_t eekLength : {2, 3, 7}) {
|
||||
SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
|
||||
generateEek(eekLength);
|
||||
generateTestEekChain(eekLength);
|
||||
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
|
||||
testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
@@ -387,30 +395,61 @@ TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an empty certificate request in prod mode. Generation will fail because we don't have a
|
||||
* valid GEEK.
|
||||
*
|
||||
* TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
|
||||
* able to decrypt.
|
||||
* Ensure that test mode outputs a unique BCC root key every time we request a
|
||||
* certificate request. Else, it's possible that the test mode API could be used
|
||||
* to fingerprint devices. Only the GEEK should be allowed to decrypt the same
|
||||
* device public key multiple times.
|
||||
*/
|
||||
TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
|
||||
bool testMode = false;
|
||||
for (size_t eekLength : {2, 3, 7}) {
|
||||
SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
|
||||
generateEek(eekLength);
|
||||
TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
|
||||
constexpr bool testMode = true;
|
||||
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
EXPECT_FALSE(status.isOk());
|
||||
EXPECT_EQ(status.getServiceSpecificError(),
|
||||
BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
std::vector<BccEntryData> firstBcc;
|
||||
checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
|
||||
&firstBcc);
|
||||
|
||||
status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
std::vector<BccEntryData> secondBcc;
|
||||
checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
|
||||
&secondBcc);
|
||||
|
||||
// Verify that none of the keys in the first BCC are repeated in the second one.
|
||||
for (const auto& i : firstBcc) {
|
||||
for (auto& j : secondBcc) {
|
||||
ASSERT_THAT(i.pubKey, testing::Not(testing::ElementsAreArray(j.pubKey)))
|
||||
<< "Found a repeated pubkey in two generateCertificateRequest test mode calls";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an empty certificate request in prod mode. This test must be run explicitly, and
|
||||
* is not run by default. Not all devices are GMS devices, and therefore they do not all
|
||||
* trust the Google EEK root.
|
||||
*/
|
||||
TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) {
|
||||
bool testMode = false;
|
||||
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
EXPECT_TRUE(status.isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a non-empty certificate request in test mode. Decrypt, parse and validate the contents.
|
||||
*/
|
||||
@@ -420,13 +459,13 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
|
||||
|
||||
for (size_t eekLength : {2, 3, 7}) {
|
||||
SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
|
||||
generateEek(eekLength);
|
||||
generateTestEekChain(eekLength);
|
||||
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
|
||||
testMode, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
@@ -435,30 +474,21 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a non-empty certificate request in prod mode. Must fail because we don't have a valid
|
||||
* GEEK.
|
||||
*
|
||||
* TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
|
||||
* able to decrypt.
|
||||
* Generate a non-empty certificate request in prod mode. This test must be run explicitly, and
|
||||
* is not run by default. Not all devices are GMS devices, and therefore they do not all
|
||||
* trust the Google EEK root.
|
||||
*/
|
||||
TEST_P(CertificateRequestTest, NonEmptyRequest_prodMode) {
|
||||
TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) {
|
||||
bool testMode = false;
|
||||
generateKeys(testMode, 4 /* numKeys */);
|
||||
|
||||
for (size_t eekLength : {2, 3, 7}) {
|
||||
SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
|
||||
generateEek(eekLength);
|
||||
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
EXPECT_FALSE(status.isOk());
|
||||
EXPECT_EQ(status.getServiceSpecificError(),
|
||||
BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
|
||||
}
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
EXPECT_TRUE(status.isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,8 +503,8 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) {
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {keyWithCorruptMac}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_FALSE(status.isOk()) << status.getMessage();
|
||||
EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
|
||||
}
|
||||
@@ -483,7 +513,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) {
|
||||
* Generate a non-empty certificate request in prod mode, but with the MAC corrupted on the keypair.
|
||||
*/
|
||||
TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) {
|
||||
bool testMode = true;
|
||||
bool testMode = false;
|
||||
generateKeys(testMode, 1 /* numKeys */);
|
||||
MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
|
||||
|
||||
@@ -491,38 +521,35 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) {
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {keyWithCorruptMac}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_FALSE(status.isOk()) << status.getMessage();
|
||||
auto rc = status.getServiceSpecificError();
|
||||
|
||||
// TODO(drysdale): drop the INVALID_EEK potential error code when a real GEEK is available.
|
||||
EXPECT_TRUE(rc == BnRemotelyProvisionedComponent::STATUS_INVALID_EEK ||
|
||||
rc == BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
|
||||
EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a non-empty certificate request in prod mode that has a corrupt EEK chain.
|
||||
* Confirm that the request is rejected.
|
||||
*
|
||||
* TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
|
||||
* implementation is checking signatures.
|
||||
*/
|
||||
TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
|
||||
bool testMode = false;
|
||||
generateKeys(testMode, 4 /* numKeys */);
|
||||
|
||||
for (size_t ii = 0; ii < eekLength_; ii++) {
|
||||
auto chain = corrupt_sig_chain(eekChain_, ii);
|
||||
auto prodEekChain = getProdEekChain();
|
||||
auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain);
|
||||
ASSERT_NE(parsedChain, nullptr) << parseErr;
|
||||
ASSERT_NE(parsedChain->asArray(), nullptr);
|
||||
|
||||
for (int ii = 0; ii < parsedChain->asArray()->size(); ++ii) {
|
||||
auto chain = corrupt_sig_chain(prodEekChain, ii);
|
||||
ASSERT_TRUE(chain) << chain.message();
|
||||
EekChain corruptEek = chain.moveValue();
|
||||
|
||||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, keysToSign_, corruptEek.chain, challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, *chain,
|
||||
challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_FALSE(status.isOk());
|
||||
ASSERT_EQ(status.getServiceSpecificError(),
|
||||
BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
|
||||
@@ -532,9 +559,6 @@ TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
|
||||
/**
|
||||
* Generate a non-empty certificate request in prod mode that has an incomplete EEK chain.
|
||||
* Confirm that the request is rejected.
|
||||
*
|
||||
* TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
|
||||
* implementation is checking signatures.
|
||||
*/
|
||||
TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) {
|
||||
bool testMode = false;
|
||||
@@ -542,7 +566,7 @@ TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) {
|
||||
|
||||
// Build an EEK chain that omits the first self-signed cert.
|
||||
auto truncatedChain = cppbor::Array();
|
||||
auto [chain, _, parseErr] = cppbor::parse(eekChain_.chain);
|
||||
auto [chain, _, parseErr] = cppbor::parse(getProdEekChain());
|
||||
ASSERT_TRUE(chain);
|
||||
auto eekChain = chain->asArray();
|
||||
ASSERT_NE(eekChain, nullptr);
|
||||
@@ -571,7 +595,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) {
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
true /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
|
||||
true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_FALSE(status.isOk());
|
||||
ASSERT_EQ(status.getServiceSpecificError(),
|
||||
@@ -589,7 +613,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) {
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
false /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
|
||||
false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_FALSE(status.isOk());
|
||||
ASSERT_EQ(status.getServiceSpecificError(),
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<configuration description="Runs VtsHalRemotelyProvisionedComponentTargetTest.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-native" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push-file"
|
||||
key="VtsHalRemotelyProvisionedComponentTargetTest"
|
||||
value="/data/local/tmp/VtsHalRemotelyProvisionedComponentTargetTest" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="module-name" value="VtsHalRemotelyProvisionedComponentTargetTest" />
|
||||
<option name="native-test-timeout" value="900000"/> <!-- 15 minutes -->
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -57,8 +57,28 @@ cc_library {
|
||||
"include",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libcrypto",
|
||||
"libjsoncpp",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libkeymint_remote_prov_support_test",
|
||||
srcs: ["remote_prov_utils_test.cpp"],
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
"libgtest_main",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libcrypto",
|
||||
"libjsoncpp",
|
||||
"libkeymaster_portable",
|
||||
"libkeymint_remote_prov_support",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -27,6 +27,31 @@ using namespace cppcose;
|
||||
|
||||
extern bytevec kTestMacKey;
|
||||
|
||||
// The Google root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1
|
||||
inline constexpr uint8_t kCoseEncodedRootCert[] = {
|
||||
0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x2a, 0xa4, 0x01, 0x01, 0x03, 0x27, 0x20, 0x06,
|
||||
0x21, 0x58, 0x20, 0x99, 0xb9, 0xee, 0xdd, 0x5e, 0xe4, 0x52, 0xf6, 0x85, 0xc6, 0x4c, 0x62,
|
||||
0xdc, 0x3e, 0x61, 0xab, 0x57, 0x48, 0x7d, 0x75, 0x37, 0x29, 0xad, 0x76, 0x80, 0x32, 0xd2,
|
||||
0xb3, 0xcb, 0x63, 0x58, 0xd9, 0x58, 0x40, 0x1e, 0x22, 0x08, 0x4b, 0xa4, 0xb7, 0xa4, 0xc8,
|
||||
0xd7, 0x4e, 0x03, 0x0e, 0xfe, 0xb8, 0xaf, 0x14, 0x4c, 0xa7, 0x3b, 0x6f, 0xa5, 0xcd, 0xdc,
|
||||
0xda, 0x79, 0xc6, 0x2b, 0x64, 0xfe, 0x99, 0x39, 0xaf, 0x76, 0xe7, 0x80, 0xfa, 0x66, 0x00,
|
||||
0x85, 0x0d, 0x07, 0x98, 0x2a, 0xac, 0x91, 0x5c, 0xa7, 0x25, 0x14, 0x49, 0x06, 0x34, 0x75,
|
||||
0xca, 0x8a, 0x27, 0x7a, 0xd9, 0xe3, 0x5a, 0x49, 0xeb, 0x02, 0x03};
|
||||
|
||||
// The Google Endpoint Encryption Key certificate, encoded as COSE_Sign1
|
||||
inline constexpr uint8_t kCoseEncodedGeekCert[] = {
|
||||
0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x4e, 0xa5, 0x01, 0x01, 0x02, 0x58, 0x20,
|
||||
0xd0, 0xae, 0xc1, 0x15, 0xca, 0x2a, 0xcf, 0x73, 0xae, 0x6b, 0xcc, 0xcb, 0xd1, 0x96,
|
||||
0x1d, 0x65, 0xe8, 0xb1, 0xdd, 0xd7, 0x4a, 0x1a, 0x37, 0xb9, 0x43, 0x3a, 0x97, 0xd5,
|
||||
0x99, 0xdf, 0x98, 0x08, 0x03, 0x38, 0x18, 0x20, 0x04, 0x21, 0x58, 0x20, 0xbe, 0x85,
|
||||
0xe7, 0x46, 0xc4, 0xa3, 0x42, 0x5a, 0x40, 0xd9, 0x36, 0x3a, 0xa6, 0x15, 0xd0, 0x2c,
|
||||
0x58, 0x7e, 0x3d, 0xdc, 0x33, 0x02, 0x32, 0xd2, 0xfc, 0x5e, 0x1e, 0x87, 0x25, 0x5f,
|
||||
0x72, 0x60, 0x58, 0x40, 0x9b, 0xcf, 0x90, 0xe2, 0x2e, 0x4b, 0xab, 0xd1, 0x18, 0xb1,
|
||||
0x0e, 0x8e, 0x5d, 0x20, 0x27, 0x4b, 0x84, 0x58, 0xfe, 0xfc, 0x32, 0x90, 0x7e, 0x72,
|
||||
0x05, 0x83, 0xbc, 0xd7, 0x82, 0xbe, 0xfa, 0x64, 0x78, 0x2d, 0x54, 0x10, 0x4b, 0xc0,
|
||||
0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2,
|
||||
0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04};
|
||||
|
||||
/**
|
||||
* Generates random bytes.
|
||||
*/
|
||||
@@ -44,6 +69,11 @@ struct EekChain {
|
||||
*/
|
||||
ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId);
|
||||
|
||||
/**
|
||||
* Returns the CBOR-encoded, production Google Endpoint Encryption Key chain.
|
||||
*/
|
||||
bytevec getProdEekChain();
|
||||
|
||||
struct BccEntryData {
|
||||
bytevec pubKey;
|
||||
};
|
||||
@@ -57,4 +87,26 @@ struct BccEntryData {
|
||||
*/
|
||||
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc);
|
||||
|
||||
struct JsonOutput {
|
||||
static JsonOutput Ok(std::string json) { return {std::move(json), ""}; }
|
||||
static JsonOutput Error(std::string error) { return {"", std::move(error)}; }
|
||||
|
||||
std::string output;
|
||||
std::string error; // if non-empty, this describes what went wrong
|
||||
};
|
||||
|
||||
/**
|
||||
* Take a given certificate request and output a JSON blob containing both the
|
||||
* build fingerprint and certificate request. This data may be serialized, then
|
||||
* later uploaded to the remote provisioning service. The input csr is not
|
||||
* validated, only encoded.
|
||||
*
|
||||
* Output format:
|
||||
* {
|
||||
* "build_fingerprint": <string>
|
||||
* "csr": <base64 CBOR CSR>
|
||||
* }
|
||||
*/
|
||||
JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr);
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
||||
|
||||
@@ -14,11 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <remote_prov/remote_prov_utils.h>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
||||
#include <android-base/properties.h>
|
||||
#include <cppbor.h>
|
||||
#include <json/json.h>
|
||||
#include <openssl/base64.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <remote_prov/remote_prov_utils.h>
|
||||
|
||||
namespace aidl::android::hardware::security::keymint::remote_prov {
|
||||
|
||||
@@ -31,6 +35,10 @@ bytevec randomBytes(size_t numBytes) {
|
||||
}
|
||||
|
||||
ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
|
||||
if (length < 2) {
|
||||
return "EEK chain must contain at least 2 certs.";
|
||||
}
|
||||
|
||||
auto eekChain = cppbor::Array();
|
||||
|
||||
bytevec prev_priv_key;
|
||||
@@ -78,6 +86,18 @@ ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
|
||||
return EekChain{eekChain.encode(), pub_key, priv_key};
|
||||
}
|
||||
|
||||
bytevec getProdEekChain() {
|
||||
bytevec prodEek;
|
||||
prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert));
|
||||
|
||||
// In CBOR encoding, 0x82 indicates an array of two items
|
||||
prodEek.push_back(0x82);
|
||||
prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert));
|
||||
prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert));
|
||||
|
||||
return prodEek;
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
|
||||
const bytevec& signingCoseKey, const bytevec& aad) {
|
||||
if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
|
||||
@@ -162,4 +182,36 @@ ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
|
||||
return result;
|
||||
}
|
||||
|
||||
JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr) {
|
||||
const std::string kFingerprintProp = "ro.build.fingerprint";
|
||||
|
||||
if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
|
||||
return JsonOutput::Error("Unable to read build fingerprint");
|
||||
}
|
||||
|
||||
bytevec csrCbor = csr.encode();
|
||||
size_t base64Length;
|
||||
int rc = EVP_EncodedLength(&base64Length, csrCbor.size());
|
||||
if (!rc) {
|
||||
return JsonOutput::Error("Error getting base64 length. Size overflow?");
|
||||
}
|
||||
|
||||
std::vector<char> base64(base64Length);
|
||||
rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), csrCbor.data(), csrCbor.size());
|
||||
++rc; // Account for NUL, which BoringSSL does not for some reason.
|
||||
if (rc != base64Length) {
|
||||
return JsonOutput::Error("Error writing base64. Expected " + std::to_string(base64Length) +
|
||||
" bytes to be written, but " + std::to_string(rc) +
|
||||
" bytes were actually written.");
|
||||
}
|
||||
|
||||
Json::Value json(Json::objectValue);
|
||||
json["build_fingerprint"] = ::android::base::GetProperty(kFingerprintProp, /*default=*/"");
|
||||
json["csr"] = base64.data(); // Boring writes a NUL-terminated c-string
|
||||
|
||||
Json::StreamWriterBuilder factory;
|
||||
factory["indentation"] = ""; // disable pretty formatting
|
||||
return JsonOutput::Ok(Json::writeString(factory, json));
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
||||
|
||||
101
security/keymint/support/remote_prov_utils_test.cpp
Normal file
101
security/keymint/support/remote_prov_utils_test.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/properties.h>
|
||||
#include <cppbor_parse.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <keymaster/android_keymaster_utils.h>
|
||||
#include <keymaster/logger.h>
|
||||
#include <keymaster/remote_provisioning_utils.h>
|
||||
#include <openssl/curve25519.h>
|
||||
#include <remote_prov/remote_prov_utils.h>
|
||||
#include <cstdint>
|
||||
#include "cppbor.h"
|
||||
#include "keymaster/cppcose/cppcose.h"
|
||||
|
||||
namespace aidl::android::hardware::security::keymint::remote_prov {
|
||||
namespace {
|
||||
|
||||
using ::keymaster::KeymasterBlob;
|
||||
using ::keymaster::validateAndExtractEekPubAndId;
|
||||
using ::testing::ElementsAreArray;
|
||||
|
||||
TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) {
|
||||
ASSERT_FALSE(generateEekChain(1, /*eekId=*/{}));
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GenerateEekChain) {
|
||||
bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0};
|
||||
for (size_t length : {2, 3, 31}) {
|
||||
auto get_eek_result = generateEekChain(length, kTestEekId);
|
||||
ASSERT_TRUE(get_eek_result) << get_eek_result.message();
|
||||
|
||||
auto& [chain, pubkey, privkey] = *get_eek_result;
|
||||
|
||||
auto validation_result = validateAndExtractEekPubAndId(
|
||||
/*testMode=*/true, KeymasterBlob(chain.data(), chain.size()));
|
||||
ASSERT_TRUE(validation_result.isOk());
|
||||
|
||||
auto& [eekPub, eekId] = *validation_result;
|
||||
EXPECT_THAT(eekId, ElementsAreArray(kTestEekId));
|
||||
EXPECT_THAT(eekPub, ElementsAreArray(pubkey));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GetProdEekChain) {
|
||||
auto chain = getProdEekChain();
|
||||
|
||||
auto validation_result = validateAndExtractEekPubAndId(
|
||||
/*testMode=*/false, KeymasterBlob(chain.data(), chain.size()));
|
||||
ASSERT_TRUE(validation_result.isOk()) << "Error: " << validation_result.moveError();
|
||||
|
||||
auto& [eekPub, eekId] = *validation_result;
|
||||
|
||||
auto [geekCert, ignoredNewPos, error] =
|
||||
cppbor::parse(kCoseEncodedGeekCert, sizeof(kCoseEncodedGeekCert));
|
||||
ASSERT_NE(geekCert, nullptr) << "Error: " << error;
|
||||
ASSERT_NE(geekCert->asArray(), nullptr);
|
||||
|
||||
auto& encodedGeekCoseKey = geekCert->asArray()->get(kCoseSign1Payload);
|
||||
ASSERT_NE(encodedGeekCoseKey, nullptr);
|
||||
ASSERT_NE(encodedGeekCoseKey->asBstr(), nullptr);
|
||||
|
||||
auto geek = CoseKey::parse(encodedGeekCoseKey->asBstr()->value());
|
||||
ASSERT_TRUE(geek) << "Error: " << geek.message();
|
||||
|
||||
const std::vector<uint8_t> empty;
|
||||
EXPECT_THAT(eekId, ElementsAreArray(geek->getBstrValue(CoseKey::KEY_ID).value_or(empty)));
|
||||
EXPECT_THAT(eekPub, ElementsAreArray(geek->getBstrValue(CoseKey::PUBKEY_X).value_or(empty)));
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, JsonEncodeCsr) {
|
||||
cppbor::Array array;
|
||||
array.add(1);
|
||||
|
||||
auto [json, error] = jsonEncodeCsrWithBuild(array);
|
||||
|
||||
ASSERT_TRUE(error.empty()) << error;
|
||||
|
||||
std::string expected = R"({"build_fingerprint":")" +
|
||||
::android::base::GetProperty("ro.build.fingerprint", /*default=*/"") +
|
||||
R"(","csr":"gQE="})";
|
||||
|
||||
ASSERT_EQ(json, expected);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
||||
@@ -268,10 +268,16 @@ TEST_F(SharedSecretAidlTest, ComputeSharedSecretShortNonce) {
|
||||
<< "Shared secret service that provided tweaked param should fail to compute "
|
||||
"shared secret";
|
||||
} else {
|
||||
EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
|
||||
EXPECT_NE(correct_response, responses[i].sharing_check)
|
||||
<< "Others should calculate a different shared secret, due to the tweaked "
|
||||
"nonce.";
|
||||
// Other services *may* succeed, or may notice the invalid size for the nonce.
|
||||
// However, if another service completes the computation, it should get the 'wrong'
|
||||
// answer.
|
||||
if (responses[i].error == ErrorCode::OK) {
|
||||
EXPECT_NE(correct_response, responses[i].sharing_check)
|
||||
<< "Others should calculate a different shared secret, due to the tweaked "
|
||||
"nonce.";
|
||||
} else {
|
||||
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,10 +354,16 @@ TEST_F(SharedSecretAidlTest, ComputeSharedSecretShortSeed) {
|
||||
<< "Shared secret service that provided tweaked param should fail to compute "
|
||||
"shared secret";
|
||||
} else {
|
||||
EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
|
||||
EXPECT_NE(correct_response, responses[i].sharing_check)
|
||||
<< "Others should calculate a different shared secret, due to the tweaked "
|
||||
"nonce.";
|
||||
// Other services *may* succeed, or may notice the invalid size for the seed.
|
||||
// However, if another service completes the computation, it should get the 'wrong'
|
||||
// answer.
|
||||
if (responses[i].error == ErrorCode::OK) {
|
||||
EXPECT_NE(correct_response, responses[i].sharing_check)
|
||||
<< "Others should calculate a different shared secret, due to the tweaked "
|
||||
"seed.";
|
||||
} else {
|
||||
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,10 @@ const std::vector<CompositePrimitive> kCompositePrimitives{
|
||||
android::enum_range<CompositePrimitive>().begin(),
|
||||
android::enum_range<CompositePrimitive>().end()};
|
||||
|
||||
const std::vector<CompositePrimitive> kOptionalPrimitives = {
|
||||
CompositePrimitive::THUD,
|
||||
CompositePrimitive::SPIN,
|
||||
const std::vector<CompositePrimitive> kRequiredPrimitives = {
|
||||
CompositePrimitive::CLICK, CompositePrimitive::LIGHT_TICK,
|
||||
CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
|
||||
CompositePrimitive::QUICK_FALL,
|
||||
};
|
||||
|
||||
const std::vector<CompositePrimitive> kInvalidPrimitives = {
|
||||
@@ -393,11 +394,11 @@ TEST_P(VibratorAidl, GetSupportedPrimitives) {
|
||||
for (auto primitive : kCompositePrimitives) {
|
||||
bool isPrimitiveSupported =
|
||||
std::find(supported.begin(), supported.end(), primitive) != supported.end();
|
||||
bool isPrimitiveOptional =
|
||||
std::find(kOptionalPrimitives.begin(), kOptionalPrimitives.end(), primitive) !=
|
||||
kOptionalPrimitives.end();
|
||||
bool isPrimitiveRequired =
|
||||
std::find(kRequiredPrimitives.begin(), kRequiredPrimitives.end(), primitive) !=
|
||||
kRequiredPrimitives.end();
|
||||
|
||||
EXPECT_TRUE(isPrimitiveSupported || isPrimitiveOptional) << toString(primitive);
|
||||
EXPECT_TRUE(isPrimitiveSupported || !isPrimitiveRequired) << toString(primitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi@1.0-service-
|
||||
interface android.hardware.wifi@1.2::IWifi default
|
||||
interface android.hardware.wifi@1.3::IWifi default
|
||||
interface android.hardware.wifi@1.4::IWifi default
|
||||
interface android.hardware.wifi@1.5::IWifi default
|
||||
oneshot
|
||||
disabled
|
||||
class hal
|
||||
|
||||
@@ -47,6 +47,11 @@ const std::list<std::vector<uint8_t>>& Ringbuffer::getData() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
void Ringbuffer::clear() {
|
||||
data_.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_5
|
||||
} // namespace wifi
|
||||
|
||||
@@ -37,6 +37,7 @@ class Ringbuffer {
|
||||
// within |maxSize_|.
|
||||
void append(const std::vector<uint8_t>& input);
|
||||
const std::list<std::vector<uint8_t>>& getData() const;
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::list<std::vector<uint8_t>> data_;
|
||||
|
||||
@@ -136,24 +136,25 @@ WifiApIface::getValidFrequenciesForBandInternal(V1_0::WifiBand band) {
|
||||
|
||||
WifiStatus WifiApIface::setMacAddressInternal(
|
||||
const std::array<uint8_t, 6>& mac) {
|
||||
bool status;
|
||||
// Support random MAC up to 2 interfaces
|
||||
if (instances_.size() == 2) {
|
||||
int rbyte = 1;
|
||||
for (auto const& intf : instances_) {
|
||||
std::array<uint8_t, 6> rmac = mac;
|
||||
// reverse the bits to avoid clision
|
||||
// reverse the bits to avoid collision
|
||||
rmac[rbyte] = 0xff - rmac[rbyte];
|
||||
status = iface_util_.lock()->setMacAddress(intf, rmac);
|
||||
if (!status) {
|
||||
if (!iface_util_.lock()->setMacAddress(intf, rmac)) {
|
||||
LOG(INFO) << "Failed to set random mac address on " << intf;
|
||||
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
|
||||
}
|
||||
rbyte++;
|
||||
}
|
||||
} else {
|
||||
status = iface_util_.lock()->setMacAddress(ifname_, mac);
|
||||
}
|
||||
if (!status) {
|
||||
// It also needs to set mac address for bridged interface, otherwise the mac
|
||||
// address of bridged interface will be changed after one of instance
|
||||
// down.
|
||||
if (!iface_util_.lock()->setMacAddress(ifname_, mac)) {
|
||||
LOG(ERROR) << "Fail to config MAC for interface " << ifname_;
|
||||
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
|
||||
}
|
||||
return createWifiStatus(WifiStatusCode::SUCCESS);
|
||||
@@ -181,6 +182,18 @@ WifiStatus WifiApIface::resetToFactoryMacAddressInternal() {
|
||||
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
// It needs to set mac address for bridged interface, otherwise the mac
|
||||
// address of the bridged interface will be changed after one of the
|
||||
// instance down. Thus we are generating a random MAC address for the
|
||||
// bridged interface even if we got the request to reset the Factory
|
||||
// MAC. Since the bridged interface is an internal interface for the
|
||||
// operation of bpf and others networking operation.
|
||||
if (!iface_util_.lock()->setMacAddress(
|
||||
ifname_, iface_util_.lock()->createRandomMacAddress())) {
|
||||
LOG(ERROR) << "Fail to config MAC for bridged interface "
|
||||
<< ifname_;
|
||||
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
|
||||
}
|
||||
} else {
|
||||
getMacResult = getFactoryMacAddressInternal(ifname_);
|
||||
LOG(DEBUG) << "Reset MAC to factory MAC on " << ifname_;
|
||||
|
||||
@@ -1948,8 +1948,8 @@ bool WifiChip::writeRingbufferFilesInternal() {
|
||||
// write ringbuffers to file
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(lock_t);
|
||||
for (const auto& item : ringbuffer_map_) {
|
||||
const Ringbuffer& cur_buffer = item.second;
|
||||
for (auto& item : ringbuffer_map_) {
|
||||
Ringbuffer& cur_buffer = item.second;
|
||||
if (cur_buffer.getData().empty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1967,6 +1967,7 @@ bool WifiChip::writeRingbufferFilesInternal() {
|
||||
PLOG(ERROR) << "Error writing to file";
|
||||
}
|
||||
}
|
||||
cur_buffer.clear();
|
||||
}
|
||||
// unique_lock unlocked here
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ bool WifiIfaceUtil::setMacAddress(const std::string& iface_name,
|
||||
event_handlers.on_state_toggle_off_on(iface_name);
|
||||
}
|
||||
if (!success) {
|
||||
LOG(ERROR) << "SetMacAddress failed.";
|
||||
LOG(ERROR) << "SetMacAddress failed on " << iface_name;
|
||||
} else {
|
||||
LOG(DEBUG) << "SetMacAddress succeeded.";
|
||||
LOG(DEBUG) << "SetMacAddress succeeded on " << iface_name;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -71,10 +71,10 @@ class WifiIfaceUtil {
|
||||
|
||||
virtual bool removeIfaceFromBridge(const std::string& br_name,
|
||||
const std::string& if_name);
|
||||
// Get a random MAC address.
|
||||
virtual std::array<uint8_t, 6> createRandomMacAddress();
|
||||
|
||||
private:
|
||||
std::array<uint8_t, 6> createRandomMacAddress();
|
||||
|
||||
std::weak_ptr<wifi_system::InterfaceTool> iface_tool_;
|
||||
std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
|
||||
std::unique_ptr<std::array<uint8_t, 6>> random_mac_address_;
|
||||
|
||||
@@ -155,5 +155,6 @@ INSTANTIATE_TEST_CASE_P(
|
||||
testing::ValuesIn(
|
||||
android::hardware::getAllHalInstanceNames(IWifi::descriptor)),
|
||||
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
|
||||
ISupplicant::descriptor))),
|
||||
android::hardware::wifi::supplicant::V1_4::ISupplicant::
|
||||
descriptor))),
|
||||
android::hardware::PrintInstanceTupleNameToString<>);
|
||||
|
||||
Reference in New Issue
Block a user