From 9dd059bd8625e2a0ab0ea3359bb71518f70650c0 Mon Sep 17 00:00:00 2001 From: Haining Chen Date: Thu, 19 Jan 2023 23:36:15 -0800 Subject: [PATCH] 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 --- .../server/BinaryTransparencyService.java | 65 +++++++- .../server/BinaryTransparencyServiceTest.java | 149 +++++++++++++++++- 2 files changed, 211 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 67f5f1b6ea045..b37652513914e 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -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; diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java index 653ed1a144c0b..0b25f38ed2822 100644 --- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java @@ -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 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 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 */ + ); + } }