Merge "Add feature flag for biometric property verification"
This commit is contained in:
@@ -62,6 +62,7 @@ import android.os.ShellCallback;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.text.TextUtils;
|
||||
import android.util.PackageUtils;
|
||||
import android.util.Slog;
|
||||
@@ -131,6 +132,10 @@ public class BinaryTransparencyService extends SystemService {
|
||||
// used for indicating newly installed MBAs that are updated (but unused currently)
|
||||
static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION =
|
||||
"enable_biometric_property_verification";
|
||||
|
||||
private static final boolean DEBUG = false; // toggle this for local debug
|
||||
|
||||
private final Context mContext;
|
||||
@@ -138,6 +143,7 @@ public class BinaryTransparencyService extends SystemService {
|
||||
// the system time (in ms) the last measurement was taken
|
||||
private long mMeasurementsLastRecordedMs;
|
||||
private PackageManagerInternal mPackageManagerInternal;
|
||||
private BiometricLogger mBiometricLogger;
|
||||
|
||||
/**
|
||||
* Guards whether or not measurements of MBA to be performed. When this change is enabled,
|
||||
@@ -1049,13 +1055,56 @@ public class BinaryTransparencyService extends SystemService {
|
||||
}
|
||||
private final BinaryTransparencyServiceImpl mServiceImpl;
|
||||
|
||||
/**
|
||||
* A wrapper of {@link FrameworkStatsLog} for easier testing
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class BiometricLogger {
|
||||
private static final String TAG = "BiometricLogger";
|
||||
|
||||
private static final BiometricLogger sInstance = new BiometricLogger();
|
||||
|
||||
private BiometricLogger() {}
|
||||
|
||||
public static BiometricLogger getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper of {@link FrameworkStatsLog}
|
||||
*
|
||||
* @param sensorId The sensorId of the biometric to be logged
|
||||
* @param modality The modality of the biometric
|
||||
* @param sensorType The sensor type of the biometric
|
||||
* @param sensorStrength The sensor strength of the biometric
|
||||
* @param componentId The component Id of a component of the biometric
|
||||
* @param hardwareVersion The hardware version of a component of the biometric
|
||||
* @param firmwareVersion The firmware version of a component of the biometric
|
||||
* @param serialNumber The serial number of a component of the biometric
|
||||
* @param softwareVersion The software version of a component of the biometric
|
||||
*/
|
||||
public void logStats(int sensorId, int modality, int sensorType, int sensorStrength,
|
||||
String componentId, String hardwareVersion, String firmwareVersion,
|
||||
String serialNumber, String softwareVersion) {
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
|
||||
sensorId, modality, sensorType, sensorStrength, componentId, hardwareVersion,
|
||||
firmwareVersion, serialNumber, softwareVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public BinaryTransparencyService(Context context) {
|
||||
this(context, BiometricLogger.getInstance());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
BinaryTransparencyService(Context context, BiometricLogger biometricLogger) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mServiceImpl = new BinaryTransparencyServiceImpl();
|
||||
mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
|
||||
mMeasurementsLastRecordedMs = 0;
|
||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
mBiometricLogger = biometricLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1301,7 +1350,7 @@ public class BinaryTransparencyService extends SystemService {
|
||||
// Note: none of the component info is a device identifier since every device of a given
|
||||
// model and build share the same biometric system info (see b/216195167)
|
||||
for (ComponentInfo componentInfo : prop.getComponentInfo()) {
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
|
||||
mBiometricLogger.logStats(
|
||||
sensorId,
|
||||
modality,
|
||||
sensorType,
|
||||
@@ -1314,7 +1363,19 @@ public class BinaryTransparencyService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void collectBiometricProperties() {
|
||||
@VisibleForTesting
|
||||
void collectBiometricProperties() {
|
||||
// Check the flag to determine whether biometric property verification is enabled. It's
|
||||
// disabled by default.
|
||||
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
|
||||
KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, false)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
|
||||
+ "DeviceConfig");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
FingerprintManager fpManager = null;
|
||||
FaceManager faceManager = null;
|
||||
|
||||
@@ -17,39 +17,85 @@
|
||||
package com.android.server;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.biometrics.ComponentInfoInternal;
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.face.FaceSensorProperties;
|
||||
import android.hardware.face.FaceSensorPropertiesInternal;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BinaryTransparencyServiceTest {
|
||||
private static final String TAG = "BinaryTransparencyServiceTest";
|
||||
|
||||
private Context mContext;
|
||||
private BinaryTransparencyService mBinaryTransparencyService;
|
||||
private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
|
||||
private DeviceConfig.Properties mOriginalBiometricsFlags;
|
||||
|
||||
@Mock
|
||||
private BinaryTransparencyService.BiometricLogger mBiometricLogger;
|
||||
@Mock
|
||||
private FingerprintManager mFpManager;
|
||||
@Mock
|
||||
private FaceManager mFaceManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mBinaryTransparencyService = new BinaryTransparencyService(mContext);
|
||||
mBinaryTransparencyService = new BinaryTransparencyService(mContext, mBiometricLogger);
|
||||
mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl();
|
||||
mOriginalBiometricsFlags = DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BIOMETRICS);
|
||||
|
||||
when(mContext.getSystemService(FingerprintManager.class)).thenReturn(mFpManager);
|
||||
when(mContext.getSystemService(FaceManager.class)).thenReturn(mFaceManager);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
try {
|
||||
DeviceConfig.setProperties(mOriginalBiometricsFlags);
|
||||
} catch (DeviceConfig.BadConfigException e) {
|
||||
Log.e(TAG, "Failed to reset biometrics flags to the original values before test. "
|
||||
+ e);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepSignedInfo() {
|
||||
@@ -120,4 +166,105 @@ public class BinaryTransparencyServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectBiometricProperties_disablesFeature() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
|
||||
BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
|
||||
Boolean.FALSE.toString(),
|
||||
false /* makeDefault */);
|
||||
|
||||
mBinaryTransparencyService.collectBiometricProperties();
|
||||
|
||||
verify(mFpManager, never()).getSensorPropertiesInternal();
|
||||
verify(mFaceManager, never()).getSensorProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectBiometricProperties_enablesFeature() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
|
||||
BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
|
||||
Boolean.TRUE.toString(),
|
||||
false /* makeDefault */);
|
||||
|
||||
mBinaryTransparencyService.collectBiometricProperties();
|
||||
|
||||
verify(mFpManager, times(1)).getSensorPropertiesInternal();
|
||||
verify(mFaceManager, times(1)).getSensorProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectBiometricProperties_enablesFeature_logsFingerprintProperties() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
|
||||
BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
|
||||
Boolean.TRUE.toString(),
|
||||
false /* makeDefault */);
|
||||
final List<FingerprintSensorPropertiesInternal> props = List.of(
|
||||
new FingerprintSensorPropertiesInternal(
|
||||
1 /* sensorId */,
|
||||
SensorProperties.STRENGTH_STRONG,
|
||||
5 /* maxEnrollmentsPerUser */,
|
||||
List.of(new ComponentInfoInternal("sensor" /* componentId */,
|
||||
"vendor/model/revision" /* hardwareVersion */,
|
||||
"1.01" /* firmwareVersion */, "00000001" /* serialNumber */,
|
||||
"" /* softwareVersion */)),
|
||||
FingerprintSensorProperties.TYPE_REAR,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */));
|
||||
when(mFpManager.getSensorPropertiesInternal()).thenReturn(props);
|
||||
|
||||
mBinaryTransparencyService.collectBiometricProperties();
|
||||
|
||||
verify(mBiometricLogger, times(1)).logStats(
|
||||
eq(1) /* sensorId */,
|
||||
eq(FrameworkStatsLog
|
||||
.BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FINGERPRINT),
|
||||
eq(FrameworkStatsLog
|
||||
.BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_REAR),
|
||||
eq(FrameworkStatsLog
|
||||
.BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_STRONG),
|
||||
eq("sensor") /* componentId */,
|
||||
eq("vendor/model/revision") /* hardwareVersion */,
|
||||
eq("1.01") /* firmwareVersion */,
|
||||
eq("00000001") /* serialNumber */,
|
||||
eq("") /* softwareVersion */
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectBiometricProperties_enablesFeature_logsFaceProperties() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
|
||||
BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
|
||||
Boolean.TRUE.toString(),
|
||||
false /* makeDefault */);
|
||||
final List<FaceSensorProperties> props = List.of(FaceSensorProperties.from(
|
||||
new FaceSensorPropertiesInternal(
|
||||
1 /* sensorId */,
|
||||
SensorProperties.STRENGTH_CONVENIENCE,
|
||||
1 /* maxEnrollmentsPerUser */,
|
||||
List.of(new ComponentInfoInternal("sensor" /* componentId */,
|
||||
"vendor/model/revision" /* hardwareVersion */,
|
||||
"1.01" /* firmwareVersion */, "00000001" /* serialNumber */,
|
||||
"" /* softwareVersion */)),
|
||||
FaceSensorProperties.TYPE_RGB,
|
||||
true /* supportsFaceDetection */,
|
||||
true /* supportsSelfIllumination */,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */)));
|
||||
when(mFaceManager.getSensorProperties()).thenReturn(props);
|
||||
|
||||
mBinaryTransparencyService.collectBiometricProperties();
|
||||
|
||||
verify(mBiometricLogger, times(1)).logStats(
|
||||
eq(1) /* sensorId */,
|
||||
eq(FrameworkStatsLog
|
||||
.BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FACE),
|
||||
eq(FrameworkStatsLog
|
||||
.BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FACE_RGB),
|
||||
eq(FrameworkStatsLog
|
||||
.BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_CONVENIENCE),
|
||||
eq("sensor") /* componentId */,
|
||||
eq("vendor/model/revision") /* hardwareVersion */,
|
||||
eq("1.01") /* firmwareVersion */,
|
||||
eq("00000001") /* serialNumber */,
|
||||
eq("") /* softwareVersion */
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user