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

Commit 2bbd33f8 authored by Haining Chen's avatar Haining Chen Committed by Android (Google) Code Review
Browse files

Merge "Add feature flag for biometric property verification"

parents c9e29b86 9dd059bd
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;
    }

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