Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9dd059bd authored by Haining Chen's avatar Haining Chen
Browse files

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
parent de006ba2
Loading
Loading
Loading
Loading
+63 −2
Original line number Diff line number Diff line
@@ -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;
+148 −1
Original line number Diff line number Diff line
@@ -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 */
        );
    }
}