Add wireframe for biometric TestApis

1) Adds BiometricTestSession and empty implementations
2) Adds TEST_BIOMETRIC permission
3) Grants Shell USE_BIOMETRIC and TEST_BIOMETRIC permission

Bug: 169459906
Test: make -j update-api && make -j

Change-Id: Iea9fd37d7234eb7e5e34a7a2698cbf0ca7a33a77
This commit is contained in:
Kevin Chyn
2020-10-01 11:51:31 -07:00
parent 526abec2f6
commit fc26e8a0e0
12 changed files with 436 additions and 5 deletions

View File

@@ -23,6 +23,7 @@ package android {
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
@@ -1223,6 +1224,35 @@ package android.graphics.drawable {
}
package android.hardware.biometrics {
public class BiometricManager {
method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession getTestSession();
}
public class BiometricTestSession implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void authenticateReject(int, int);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void authenticateSuccess(int, int);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void close();
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enableTestHal(int, boolean);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enrollFinish(int, int);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enrollStart(int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void internalCleanup(int, int);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyAcquired(int, int);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyError(int, int);
}
public class SensorProperties {
method public int getSensorId();
method public int getSensorStrength();
field public static final int STRENGTH_CONVENIENCE = 0; // 0x0
field public static final int STRENGTH_STRONG = 2; // 0x2
field public static final int STRENGTH_WEAK = 1; // 0x1
}
}
package android.hardware.camera2 {
public abstract class CameraDevice implements java.lang.AutoCloseable {
@@ -1328,6 +1358,14 @@ package android.hardware.display {
}
package android.hardware.fingerprint {
@Deprecated public class FingerprintManager {
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession getTestSession();
}
}
package android.hardware.hdmi {
public final class HdmiControlManager {

View File

@@ -16,14 +16,17 @@
package android.hardware.biometrics;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
import android.security.keystore.KeyGenParameterSpec;
@@ -195,6 +198,17 @@ public class BiometricManager {
mService = service;
}
/**
* Retrieves a test session for BiometricManager/BiometricPrompt.
* @hide
*/
@TestApi
@NonNull
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession getTestSession() {
return null; // TODO(169459906)
}
/**
* Determine if biometrics can be used. In other words, determine if
* {@link BiometricPrompt} can be expected to be shown (hardware available, templates enrolled,

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.biometrics;
import static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
* {@link android.hardware.fingerprint.FingerprintManager}.
* @hide
*/
@TestApi
public class BiometricTestSession implements AutoCloseable {
private static final String TAG = "TestManager";
private final Context mContext;
private final ITestService mTestService;
/**
* @hide
*/
public BiometricTestSession(@NonNull Context context, @NonNull ITestService testService) {
mContext = context;
mTestService = testService;
}
/**
* @return A list of {@link SensorProperties}
*/
@NonNull
@RequiresPermission(TEST_BIOMETRIC)
public List<SensorProperties> getSensorProperties() {
try {
final List<SensorPropertiesInternal> internalProps =
mTestService.getSensorPropertiesInternal(mContext.getOpPackageName());
final List<SensorProperties> props = new ArrayList<>();
for (SensorPropertiesInternal internalProp : internalProps) {
props.add(new SensorProperties(internalProp.sensorId, internalProp.sensorStrength));
}
return props;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
* any methods on the real HAL implementation. This allows the framework to test a substantial
* portion of the framework code that would otherwise require human interaction. Note that
* secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
* equivalent for the secret key.
*
* @param sensorId Sensor that this command applies to.
* @param enableTestHal If true, enable testing with a fake HAL instead of the real HAL.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void enableTestHal(int sensorId, boolean enableTestHal) {
try {
mTestService.enableTestHal(sensorId, enableTestHal);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Starts the enrollment process. This should generally be used when the test HAL is enabled.
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void enrollStart(int sensorId, int userId) {
try {
mTestService.enrollStart(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Finishes the enrollment process. Simulates the HAL's callback.
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void enrollFinish(int sensorId, int userId) {
try {
mTestService.enrollFinish(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Simulates a successful authentication, but does not provide a valid HAT.
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void authenticateSuccess(int sensorId, int userId) {
try {
mTestService.authenticateSuccess(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Simulates a rejected attempt.
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void authenticateReject(int sensorId, int userId) {
try {
mTestService.authenticateReject(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Simulates an acquired message from the HAL.
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void notifyAcquired(int sensorId, int userId) {
try {
mTestService.notifyAcquired(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Simulates an error message from the HAL.
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void notifyError(int sensorId, int userId) {
try {
mTestService.notifyError(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
/**
* Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
* that isn't known by both sides are deleted. This should generally be used when the test
* HAL is disabled (e.g. to clean up after a test).
*
* @param sensorId Sensor that this command applies to.
* @param userId User that this command applies to.
*/
@RequiresPermission(TEST_BIOMETRIC)
public void internalCleanup(int sensorId, int userId) {
try {
mTestService.internalCleanup(sensorId, userId);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
@Override
@RequiresPermission(TEST_BIOMETRIC)
public void close() {
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.biometrics;
import android.hardware.biometrics.SensorPropertiesInternal;
/**
* A test service for FingerprintManager and BiometricPrompt.
* @hide
*/
interface ITestService {
// Returns a list of sensor properties supported by the interface.
List<SensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
// Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
// any methods on the real HAL implementation. This allows the framework to test a substantial
// portion of the framework code that would otherwise require human interaction. Note that
// secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
// equivalent for the secret key.
void enableTestHal(int sensorId, boolean enableTestHal);
// Starts the enrollment process. This should generally be used when the test HAL is enabled.
void enrollStart(int sensorId, int userId);
// Finishes the enrollment process. Simulates the HAL's callback.
void enrollFinish(int sensorId, int userId);
// Simulates a successful authentication, but does not provide a valid HAT.
void authenticateSuccess(int sensorId, int userId);
// Simulates a rejected attempt.
void authenticateReject(int sensorId, int userId);
// Simulates an acquired message from the HAL.
void notifyAcquired(int sensorId, int userId);
// Simulates an error message from the HAL.
void notifyError(int sensorId, int userId);
// Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
// that isn't known by both sides are deleted. This should generally be used when the test
// HAL is disabled (e.g. to clean up after a test).
void internalCleanup(int sensorId, int userId);
}

View File

@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.annotation.IntDef;
import android.annotation.TestApi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -25,19 +26,18 @@ import java.lang.annotation.RetentionPolicy;
* The base class containing all modality-agnostic information.
* @hide
*/
@TestApi
public class SensorProperties {
/**
* A sensor that meets the requirements for Class 1 biometrics as defined in the CDD. This does
* not correspond to a public BiometricManager.Authenticators constant. Sensors of this strength
* are not available to applications via the public API surface.
* @hide
*/
public static final int STRENGTH_CONVENIENCE = 0;
/**
* A sensor that meets the requirements for Class 2 biometrics as defined in the CDD.
* Corresponds to BiometricManager.Authenticators.BIOMETRIC_WEAK.
* @hide
*/
public static final int STRENGTH_WEAK = 1;
@@ -46,7 +46,6 @@ public class SensorProperties {
* Corresponds to BiometricManager.Authenticators.BIOMETRIC_STRONG.
*
* Notably, this is the only strength that allows generation of HardwareAuthToken(s).
* @hide
*/
public static final int STRENGTH_STRONG = 2;
@@ -70,7 +69,6 @@ public class SensorProperties {
/**
* @return The sensor's unique identifier.
* @hide
*/
public int getSensorId() {
return mSensorId;
@@ -78,7 +76,6 @@ public class SensorProperties {
/**
* @return The sensor's strength.
* @hide
*/
@Strength
public int getSensorStrength() {

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.biometrics;
parcelable SensorPropertiesInternal;

View File

@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -28,6 +29,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -35,6 +37,7 @@ import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricTestSession;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
import android.os.CancellationSignal;
@@ -94,6 +97,22 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
/**
* Retrieves a test session for FingerprintManager.
* @hide
*/
@TestApi
@NonNull
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession getTestSession() {
try {
return new BiometricTestSession(mContext,
mService.getTestService(mContext.getOpPackageName()));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private class OnEnrollCancelListener implements OnCancelListener {
@Override
public void onCancel() {

View File

@@ -17,6 +17,7 @@ package android.hardware.fingerprint;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.ITestService;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -30,6 +31,10 @@ import java.util.List;
* @hide
*/
interface IFingerprintService {
// Retrieves a test service
ITestService getTestService(String opPackageName);
// Retrieve static sensor properties for all fingerprint sensors
List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);

View File

@@ -4541,6 +4541,12 @@
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signature" />
<!-- Allows access to TestApis for various components in the biometric stack, including
FingerprintService, FaceService, BiometricService. Used by com.android.server.biometrics
CTS tests. @hide @TestApi -->
<permission android:name="android.permission.TEST_BIOMETRIC"
android:protectionLevel="signature" />
<!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_BIOMETRIC"
android:protectionLevel="signature" />

View File

@@ -437,6 +437,9 @@ applications that come with the platform
<permission name="android.permission.MANAGE_DEBUGGING" />
<!-- Permissions required for CTS test - TimeManagerTest -->
<permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
<!-- Permissions required for CTS test - android.server.biometrics -->
<permission name="android.permission.USE_BIOMETRIC" />
<permission name="android.permission.TEST_BIOMETRIC" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">

View File

@@ -329,6 +329,12 @@
<!-- Permission needed for CTS test - TimeManagerTest -->
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
<!-- Permission required for CTS test - android.server.biometrics -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!-- Permission required for CTS test - android.server.biometrics -->
<uses-permission android:name="android.permission.TEST_BIOMETRIC" />
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"

View File

@@ -20,6 +20,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -34,6 +35,8 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.biometrics.ITestService;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
@@ -88,11 +91,74 @@ public class FingerprintService extends SystemService {
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull private List<ServiceProvider> mServiceProviders;
@Nullable private TestService mTestService;
private final class TestService extends ITestService.Stub {
@Override
public List<SensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
return null;
}
@Override
public void enableTestHal(int sensorId, boolean enableTestHal) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void enrollStart(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void enrollFinish(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void authenticateSuccess(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void authenticateReject(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void notifyAcquired(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void notifyError(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
@Override
public void internalCleanup(int sensorId, int userId) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
}
/**
* Receives the incoming binder calls from FingerprintManager.
*/
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
@Override
public ITestService getTestService(String opPackageName) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
synchronized (this) {
if (mTestService == null) {
mTestService = new TestService();
}
}
return mTestService;
}
@Override // Binder call
public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {