Add feature flag for biometric property verification

The feature flag enable_biometric_property_verification under the
biometrics namespace is checked before collecting/verifying biometric
sensor properties.

If the flag is false (the default), no data will be logged; otherwise if
the flag is true.

Bug: 259566704
Bug: 204801320
Test: atest BinaryTransparencyServiceTest
Test: Mannually set the flag to true/false via:
      adb shell device_config put biometrics
          enable_biometric_property_verification true/false
      Then check whether data is logged via:
      m statsd_testdrive && statsd_testdrive 587
Change-Id: Ic0e6fe00409c11a4f30748598b73de1a7c7d7422
This commit is contained in:
Haining Chen
2023-01-19 23:36:15 -08:00
parent de006ba2e5
commit 9dd059bd86
2 changed files with 211 additions and 3 deletions

View File

@@ -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;
}
/**
@@ -1311,7 +1360,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,
@@ -1324,7 +1373,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;

View File

@@ -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 */
);
}
}