Loading services/core/java/com/android/server/BinaryTransparencyService.java +63 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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; } /** Loading Loading @@ -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, Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java +148 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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 */ ); } } Loading
services/core/java/com/android/server/BinaryTransparencyService.java +63 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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; } /** Loading Loading @@ -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, Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java +148 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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 */ ); } }