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

Commit a3e0eab2 authored by James Willcox's avatar James Willcox Committed by Gerrit Code Review
Browse files

Merge changes from topic "cherrypicker-L39600000963568021:N90000001414828271" into main

* changes:
  Face auth  not eligible if camera in use
  Make face auth ineligible when camera access is disabled
parents 5a389aa6 3460c48b
Loading
Loading
Loading
Loading
+32 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics;

/**
 * Interface for biometrics to get camera status.
 */
public interface BiometricCameraManager {
    /**
     * Returns true if any camera is in use.
     */
    boolean isAnyCameraUnavailable();

    /**
     * Returns true if privacy is enabled and camera access is disabled.
     */
    boolean isCameraPrivacyEnabled();
}
+68 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics;

import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;

import android.annotation.NonNull;
import android.hardware.SensorPrivacyManager;
import android.hardware.camera2.CameraManager;

import java.util.concurrent.ConcurrentHashMap;

public class BiometricCameraManagerImpl implements BiometricCameraManager {

    private final CameraManager mCameraManager;
    private final SensorPrivacyManager mSensorPrivacyManager;
    private final ConcurrentHashMap<String, Boolean> mIsCameraAvailable = new ConcurrentHashMap<>();

    private final CameraManager.AvailabilityCallback mCameraAvailabilityCallback =
            new CameraManager.AvailabilityCallback() {
                @Override
                public void onCameraAvailable(@NonNull String cameraId) {
                    mIsCameraAvailable.put(cameraId, true);
                }

                @Override
                public void onCameraUnavailable(@NonNull String cameraId) {
                    mIsCameraAvailable.put(cameraId, false);
                }
            };

    public BiometricCameraManagerImpl(@NonNull CameraManager cameraManager,
            @NonNull SensorPrivacyManager sensorPrivacyManager) {
        mCameraManager = cameraManager;
        mSensorPrivacyManager = sensorPrivacyManager;
        mCameraManager.registerAvailabilityCallback(mCameraAvailabilityCallback, null);
    }

    @Override
    public boolean isAnyCameraUnavailable() {
        for (String cameraId : mIsCameraAvailable.keySet()) {
            if (!mIsCameraAvailable.get(cameraId)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean isCameraPrivacyEnabled() {
        return mSensorPrivacyManager != null && mSensorPrivacyManager
                .isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, CAMERA);
    }
}
+13 −5
Original line number Original line Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt;
@@ -47,6 +48,7 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.camera2.CameraManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.net.Uri;
import android.net.Uri;
@@ -124,6 +126,8 @@ public class BiometricService extends SystemService {
    AuthSession mAuthSession;
    AuthSession mAuthSession;
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private final Handler mHandler = new Handler(Looper.getMainLooper());


    private final BiometricCameraManager mBiometricCameraManager;

    /**
    /**
     * Tracks authenticatorId invalidation. For more details, see
     * Tracks authenticatorId invalidation. For more details, see
     * {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
     * {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
@@ -933,7 +937,7 @@ public class BiometricService extends SystemService {


        return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
        return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
                userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */,
                userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */,
                getContext());
                getContext(), mBiometricCameraManager);
    }
    }


    /**
    /**
@@ -1026,6 +1030,11 @@ public class BiometricService extends SystemService {
        public UserManager getUserManager(Context context) {
        public UserManager getUserManager(Context context) {
            return context.getSystemService(UserManager.class);
            return context.getSystemService(UserManager.class);
        }
        }

        public BiometricCameraManager getBiometricCameraManager(Context context) {
            return new BiometricCameraManagerImpl(context.getSystemService(CameraManager.class),
                    context.getSystemService(SensorPrivacyManager.class));
        }
    }
    }


    /**
    /**
@@ -1054,6 +1063,7 @@ public class BiometricService extends SystemService {
        mRequestCounter = mInjector.getRequestGenerator();
        mRequestCounter = mInjector.getRequestGenerator();
        mBiometricContext = injector.getBiometricContext(context);
        mBiometricContext = injector.getBiometricContext(context);
        mUserManager = injector.getUserManager(context);
        mUserManager = injector.getUserManager(context);
        mBiometricCameraManager = injector.getBiometricCameraManager(context);


        try {
        try {
            injector.getActivityManagerService().registerUserSwitchObserver(
            injector.getActivityManagerService().registerUserSwitchObserver(
@@ -1290,7 +1300,7 @@ public class BiometricService extends SystemService {
                final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
                final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
                        mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
                        mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
                        opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
                        opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
                        getContext());
                        getContext(), mBiometricCameraManager);


                final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
                final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();


@@ -1300,9 +1310,7 @@ public class BiometricService extends SystemService {
                        + promptInfo.isIgnoreEnrollmentState());
                        + promptInfo.isIgnoreEnrollmentState());
                // BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED is added so that BiometricPrompt can
                // BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED is added so that BiometricPrompt can
                // be shown for this case.
                // be shown for this case.
                if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS
                if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
                        || preAuthStatus.second
                        == BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED) {
                    // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
                    // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
                    // CREDENTIAL is requested and available, set the bundle to only request
                    // CREDENTIAL is requested and available, set the bundle to only request
                    // CREDENTIAL.
                    // CREDENTIAL.
+21 −20
Original line number Original line Diff line number Diff line
@@ -27,7 +27,6 @@ import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.PromptInfo;
@@ -73,13 +72,16 @@ class PreAuthInfo {
    final Context context;
    final Context context;
    private final boolean mBiometricRequested;
    private final boolean mBiometricRequested;
    private final int mBiometricStrengthRequested;
    private final int mBiometricStrengthRequested;
    private final BiometricCameraManager mBiometricCameraManager;

    private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
    private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
            boolean credentialRequested, List<BiometricSensor> eligibleSensors,
            boolean credentialRequested, List<BiometricSensor> eligibleSensors,
            List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
            List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
            boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
            boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
            Context context) {
            Context context, BiometricCameraManager biometricCameraManager) {
        mBiometricRequested = biometricRequested;
        mBiometricRequested = biometricRequested;
        mBiometricStrengthRequested = biometricStrengthRequested;
        mBiometricStrengthRequested = biometricStrengthRequested;
        mBiometricCameraManager = biometricCameraManager;
        this.credentialRequested = credentialRequested;
        this.credentialRequested = credentialRequested;


        this.eligibleSensors = eligibleSensors;
        this.eligibleSensors = eligibleSensors;
@@ -96,7 +98,8 @@ class PreAuthInfo {
            BiometricService.SettingObserver settingObserver,
            BiometricService.SettingObserver settingObserver,
            List<BiometricSensor> sensors,
            List<BiometricSensor> sensors,
            int userId, PromptInfo promptInfo, String opPackageName,
            int userId, PromptInfo promptInfo, String opPackageName,
            boolean checkDevicePolicyManager, Context context)
            boolean checkDevicePolicyManager, Context context,
            BiometricCameraManager biometricCameraManager)
            throws RemoteException {
            throws RemoteException {


        final boolean confirmationRequested = promptInfo.isConfirmationRequested();
        final boolean confirmationRequested = promptInfo.isConfirmationRequested();
@@ -124,7 +127,7 @@ class PreAuthInfo {
                        checkDevicePolicyManager, requestedStrength,
                        checkDevicePolicyManager, requestedStrength,
                        promptInfo.getAllowedSensorIds(),
                        promptInfo.getAllowedSensorIds(),
                        promptInfo.isIgnoreEnrollmentState(),
                        promptInfo.isIgnoreEnrollmentState(),
                        context);
                        biometricCameraManager);


                Slog.d(TAG, "Package: " + opPackageName
                Slog.d(TAG, "Package: " + opPackageName
                        + " Sensor ID: " + sensor.id
                        + " Sensor ID: " + sensor.id
@@ -138,7 +141,7 @@ class PreAuthInfo {
                //
                //
                // Note: if only a certain sensor is required and the privacy is enabled,
                // Note: if only a certain sensor is required and the privacy is enabled,
                // canAuthenticate() will return false.
                // canAuthenticate() will return false.
                if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) {
                if (status == AUTHENTICATOR_OK) {
                    eligibleSensors.add(sensor);
                    eligibleSensors.add(sensor);
                } else {
                } else {
                    ineligibleSensors.add(new Pair<>(sensor, status));
                    ineligibleSensors.add(new Pair<>(sensor, status));
@@ -148,7 +151,7 @@ class PreAuthInfo {


        return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
        return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
                eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
                eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
                promptInfo.isIgnoreEnrollmentState(), userId, context);
                promptInfo.isIgnoreEnrollmentState(), userId, context, biometricCameraManager);
    }
    }


    /**
    /**
@@ -165,12 +168,16 @@ class PreAuthInfo {
            BiometricSensor sensor, int userId, String opPackageName,
            BiometricSensor sensor, int userId, String opPackageName,
            boolean checkDevicePolicyManager, int requestedStrength,
            boolean checkDevicePolicyManager, int requestedStrength,
            @NonNull List<Integer> requestedSensorIds,
            @NonNull List<Integer> requestedSensorIds,
            boolean ignoreEnrollmentState, Context context) {
            boolean ignoreEnrollmentState, BiometricCameraManager biometricCameraManager) {


        if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
        if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
            return BIOMETRIC_NO_HARDWARE;
            return BIOMETRIC_NO_HARDWARE;
        }
        }


        if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) {
            return BIOMETRIC_HARDWARE_NOT_DETECTED;
        }

        final boolean wasStrongEnough =
        final boolean wasStrongEnough =
                Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
                Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
        final boolean isStrongEnough =
        final boolean isStrongEnough =
@@ -191,12 +198,10 @@ class PreAuthInfo {
                    && !ignoreEnrollmentState) {
                    && !ignoreEnrollmentState) {
                return BIOMETRIC_NOT_ENROLLED;
                return BIOMETRIC_NOT_ENROLLED;
            }
            }
            final SensorPrivacyManager sensorPrivacyManager = context
                    .getSystemService(SensorPrivacyManager.class);


            if (sensorPrivacyManager != null && sensor.modality == TYPE_FACE) {
            if (biometricCameraManager != null && sensor.modality == TYPE_FACE) {
                if (sensorPrivacyManager
                if (biometricCameraManager.isCameraPrivacyEnabled()) {
                        .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) {
                    //Camera privacy is enabled as the access is disabled
                    return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
                    return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
                }
                }
            }
            }
@@ -292,13 +297,9 @@ class PreAuthInfo {
        @AuthenticatorStatus final int status;
        @AuthenticatorStatus final int status;
        @BiometricAuthenticator.Modality int modality = TYPE_NONE;
        @BiometricAuthenticator.Modality int modality = TYPE_NONE;


        final SensorPrivacyManager sensorPrivacyManager = context
                .getSystemService(SensorPrivacyManager.class);

        boolean cameraPrivacyEnabled = false;
        boolean cameraPrivacyEnabled = false;
        if (sensorPrivacyManager != null) {
        if (mBiometricCameraManager != null) {
            cameraPrivacyEnabled = sensorPrivacyManager
            cameraPrivacyEnabled = mBiometricCameraManager.isCameraPrivacyEnabled();
                    .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId);
        }
        }


        if (mBiometricRequested && credentialRequested) {
        if (mBiometricRequested && credentialRequested) {
@@ -315,7 +316,7 @@ class PreAuthInfo {
                    // and the face sensor privacy is enabled then return
                    // and the face sensor privacy is enabled then return
                    // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
                    // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
                    //
                    //
                    // Note: This sensor will still be eligible for calls to authenticate.
                    // Note: This sensor will not be eligible for calls to authenticate.
                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
                } else {
                } else {
                    status = AUTHENTICATOR_OK;
                    status = AUTHENTICATOR_OK;
@@ -340,7 +341,7 @@ class PreAuthInfo {
                    // If the only modality requested is face and the privacy is enabled
                    // If the only modality requested is face and the privacy is enabled
                    // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
                    // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
                    //
                    //
                    // Note: This sensor will still be eligible for calls to authenticate.
                    // Note: This sensor will not be eligible for calls to authenticate.
                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
                } else {
                } else {
                    status = AUTHENTICATOR_OK;
                    status = AUTHENTICATOR_OK;
+3 −1
Original line number Original line Diff line number Diff line
@@ -104,6 +104,7 @@ public class AuthSessionTest {
    @Mock private KeyStore mKeyStore;
    @Mock private KeyStore mKeyStore;
    @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
    @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
    @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
    @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
    @Mock private BiometricCameraManager mBiometricCameraManager;


    private Random mRandom;
    private Random mRandom;
    private IBinder mToken;
    private IBinder mToken;
@@ -571,7 +572,8 @@ public class AuthSessionTest {
                promptInfo,
                promptInfo,
                TEST_PACKAGE,
                TEST_PACKAGE,
                checkDevicePolicyManager,
                checkDevicePolicyManager,
                mContext);
                mContext,
                mBiometricCameraManager);
    }
    }


    private AuthSession createAuthSession(List<BiometricSensor> sensors,
    private AuthSession createAuthSession(List<BiometricSensor> sensors,
Loading