Merge changes from topic "faceCameraSensorPrivacy" into sc-v2-dev am: 698f40caf7
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16285463 Change-Id: I9982667e2035ad3d88a614b4ed5e8d5747c42ed4
This commit is contained in:
@@ -150,6 +150,12 @@ public interface BiometricConstants {
|
|||||||
*/
|
*/
|
||||||
int BIOMETRIC_ERROR_RE_ENROLL = 16;
|
int BIOMETRIC_ERROR_RE_ENROLL = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The privacy setting has been enabled and will block use of the sensor.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
int BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED = 18;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
|
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
|
||||||
* because the authentication attempt was unsuccessful.
|
* because the authentication attempt was unsuccessful.
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public interface BiometricFaceConstants {
|
|||||||
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
|
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
|
||||||
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
|
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
|
||||||
BIOMETRIC_ERROR_RE_ENROLL,
|
BIOMETRIC_ERROR_RE_ENROLL,
|
||||||
FACE_ERROR_UNKNOWN
|
FACE_ERROR_UNKNOWN,
|
||||||
})
|
})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@interface FaceError {}
|
@interface FaceError {}
|
||||||
|
|||||||
@@ -1752,6 +1752,8 @@
|
|||||||
<string name="face_setup_notification_title">Set up Face Unlock</string>
|
<string name="face_setup_notification_title">Set up Face Unlock</string>
|
||||||
<!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
|
<!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
|
||||||
<string name="face_setup_notification_content">Unlock your phone by looking at it</string>
|
<string name="face_setup_notification_content">Unlock your phone by looking at it</string>
|
||||||
|
<!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
|
||||||
<!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
|
<!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
|
||||||
<string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
|
<string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
|
||||||
<!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
|
<!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
|
||||||
|
|||||||
@@ -2570,6 +2570,7 @@
|
|||||||
<java-symbol type="string" name="face_recalibrate_notification_name" />
|
<java-symbol type="string" name="face_recalibrate_notification_name" />
|
||||||
<java-symbol type="string" name="face_recalibrate_notification_title" />
|
<java-symbol type="string" name="face_recalibrate_notification_title" />
|
||||||
<java-symbol type="string" name="face_recalibrate_notification_content" />
|
<java-symbol type="string" name="face_recalibrate_notification_content" />
|
||||||
|
<java-symbol type="string" name="face_sensor_privacy_enabled" />
|
||||||
<java-symbol type="string" name="face_error_unable_to_process" />
|
<java-symbol type="string" name="face_error_unable_to_process" />
|
||||||
<java-symbol type="string" name="face_error_hw_not_available" />
|
<java-symbol type="string" name="face_error_hw_not_available" />
|
||||||
<java-symbol type="string" name="face_error_no_space" />
|
<java-symbol type="string" name="face_error_no_space" />
|
||||||
|
|||||||
@@ -219,6 +219,9 @@
|
|||||||
<!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
|
<!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
|
||||||
<string name="kg_face_not_recognized">Not recognized</string>
|
<string name="kg_face_not_recognized">Not recognized</string>
|
||||||
|
|
||||||
|
<!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="kg_face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
|
||||||
|
|
||||||
<!-- Instructions telling the user remaining times when enter SIM PIN view. -->
|
<!-- Instructions telling the user remaining times when enter SIM PIN view. -->
|
||||||
<plurals name="kg_password_default_pin_message">
|
<plurals name="kg_password_default_pin_message">
|
||||||
<item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
|
<item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
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.BiometricManager;
|
import android.hardware.biometrics.BiometricManager;
|
||||||
import android.hardware.biometrics.BiometricSourceType;
|
import android.hardware.biometrics.BiometricSourceType;
|
||||||
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
|
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
|
||||||
@@ -335,6 +336,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
private boolean mLockIconPressed;
|
private boolean mLockIconPressed;
|
||||||
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||||
private final Executor mBackgroundExecutor;
|
private final Executor mBackgroundExecutor;
|
||||||
|
private SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
private int mFaceAuthUserId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Short delay before restarting fingerprint authentication after a successful try. This should
|
* Short delay before restarting fingerprint authentication after a successful try. This should
|
||||||
@@ -1016,6 +1019,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
|
|
||||||
// Error is always the end of authentication lifecycle
|
// Error is always the end of authentication lifecycle
|
||||||
mFaceCancelSignal = null;
|
mFaceCancelSignal = null;
|
||||||
|
boolean cameraPrivacyEnabled = false;
|
||||||
|
if (mSensorPrivacyManager != null) {
|
||||||
|
cameraPrivacyEnabled = mSensorPrivacyManager
|
||||||
|
.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
|
||||||
|
mFaceAuthUserId);
|
||||||
|
}
|
||||||
|
|
||||||
if (msgId == FaceManager.FACE_ERROR_CANCELED
|
if (msgId == FaceManager.FACE_ERROR_CANCELED
|
||||||
&& mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
|
&& mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
|
||||||
@@ -1025,7 +1034,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
|
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE
|
final boolean isHwUnavailable = msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE;
|
||||||
|
|
||||||
|
if (isHwUnavailable
|
||||||
|| msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
|
|| msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
|
||||||
if (mHardwareFaceUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
|
if (mHardwareFaceUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
|
||||||
mHardwareFaceUnavailableRetryCount++;
|
mHardwareFaceUnavailableRetryCount++;
|
||||||
@@ -1041,6 +1052,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
requireStrongAuthIfAllLockedOut();
|
requireStrongAuthIfAllLockedOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isHwUnavailable && cameraPrivacyEnabled) {
|
||||||
|
errString = mContext.getString(R.string.kg_face_sensor_privacy_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||||
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
|
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
|
||||||
if (cb != null) {
|
if (cb != null) {
|
||||||
@@ -1816,6 +1831,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
mLockPatternUtils = lockPatternUtils;
|
mLockPatternUtils = lockPatternUtils;
|
||||||
mAuthController = authController;
|
mAuthController = authController;
|
||||||
dumpManager.registerDumpable(getClass().getName(), this);
|
dumpManager.registerDumpable(getClass().getName(), this);
|
||||||
|
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
|
||||||
|
|
||||||
mHandler = new Handler(mainLooper) {
|
mHandler = new Handler(mainLooper) {
|
||||||
@Override
|
@Override
|
||||||
@@ -2517,6 +2533,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
|||||||
// This would need to be updated for multi-sensor devices
|
// This would need to be updated for multi-sensor devices
|
||||||
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
|
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
|
||||||
&& mFaceSensorProperties.get(0).supportsFaceDetection;
|
&& mFaceSensorProperties.get(0).supportsFaceDetection;
|
||||||
|
mFaceAuthUserId = userId;
|
||||||
if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
|
if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
|
||||||
mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
|
mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.content.IntentFilter;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
|
import android.hardware.SensorPrivacyManager;
|
||||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||||
import android.hardware.biometrics.BiometricConstants;
|
import android.hardware.biometrics.BiometricConstants;
|
||||||
import android.hardware.biometrics.BiometricManager.Authenticators;
|
import android.hardware.biometrics.BiometricManager.Authenticators;
|
||||||
@@ -89,6 +90,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
|||||||
|
|
||||||
private static final String TAG = "AuthController";
|
private static final String TAG = "AuthController";
|
||||||
private static final boolean DEBUG = true;
|
private static final boolean DEBUG = true;
|
||||||
|
private static final int SENSOR_PRIVACY_DELAY = 500;
|
||||||
|
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
private final CommandQueue mCommandQueue;
|
private final CommandQueue mCommandQueue;
|
||||||
@@ -122,6 +124,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
|||||||
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
|
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
|
||||||
|
|
||||||
@NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
|
@NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
|
||||||
|
private SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
|
||||||
private class BiometricTaskStackListener extends TaskStackListener {
|
private class BiometricTaskStackListener extends TaskStackListener {
|
||||||
@Override
|
@Override
|
||||||
@@ -492,6 +495,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
|||||||
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||||
|
|
||||||
context.registerReceiver(mBroadcastReceiver, filter);
|
context.registerReceiver(mBroadcastReceiver, filter);
|
||||||
|
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFingerprintLocation() {
|
private void updateFingerprintLocation() {
|
||||||
@@ -642,10 +646,16 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
|||||||
final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
|
final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
|
||||||
|| (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
|
|| (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
|
||||||
|
|
||||||
|
boolean isCameraPrivacyEnabled = false;
|
||||||
|
if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||||
|
&& mSensorPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
|
||||||
|
mCurrentDialogArgs.argi1 /* userId */)) {
|
||||||
|
isCameraPrivacyEnabled = true;
|
||||||
|
}
|
||||||
// TODO(b/141025588): Create separate methods for handling hard and soft errors.
|
// TODO(b/141025588): Create separate methods for handling hard and soft errors.
|
||||||
final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
|
final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
|
||||||
|| error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
|
|| error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
|
||||||
|
|| isCameraPrivacyEnabled);
|
||||||
if (mCurrentDialog != null) {
|
if (mCurrentDialog != null) {
|
||||||
if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
|
if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
|
||||||
if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
|
if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
|
||||||
@@ -655,12 +665,23 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
|||||||
? mContext.getString(R.string.biometric_not_recognized)
|
? mContext.getString(R.string.biometric_not_recognized)
|
||||||
: getErrorString(modality, error, vendorCode);
|
: getErrorString(modality, error, vendorCode);
|
||||||
if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
|
if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
|
||||||
|
// The camera privacy error can return before the prompt initializes its state,
|
||||||
|
// causing the prompt to appear to endlessly authenticate. Add a small delay
|
||||||
|
// to stop this.
|
||||||
|
if (isCameraPrivacyEnabled) {
|
||||||
|
mHandler.postDelayed(() -> {
|
||||||
|
mCurrentDialog.onAuthenticationFailed(modality,
|
||||||
|
mContext.getString(R.string.face_sensor_privacy_enabled));
|
||||||
|
}, SENSOR_PRIVACY_DELAY);
|
||||||
|
} else {
|
||||||
mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
|
mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final String errorMessage = getErrorString(modality, error, vendorCode);
|
final String errorMessage = getErrorString(modality, error, vendorCode);
|
||||||
if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
|
if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
|
||||||
mCurrentDialog.onError(modality, errorMessage);
|
mCurrentDialog.onError(modality, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "onBiometricError callback but dialog is gone");
|
Log.w(TAG, "onBiometricError callback but dialog is gone");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1036,7 +1036,8 @@ public class BiometricService extends SystemService {
|
|||||||
promptInfo.setAuthenticators(authenticators);
|
promptInfo.setAuthenticators(authenticators);
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1375,7 +1376,8 @@ public class BiometricService extends SystemService {
|
|||||||
try {
|
try {
|
||||||
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());
|
||||||
|
|
||||||
final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
|
final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
|
||||||
|
|
||||||
@@ -1383,8 +1385,11 @@ public class BiometricService extends SystemService {
|
|||||||
+ "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
|
+ "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
|
||||||
+ " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
|
+ " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
|
||||||
+ promptInfo.isIgnoreEnrollmentState());
|
+ promptInfo.isIgnoreEnrollmentState());
|
||||||
|
// BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED is added so that BiometricPrompt can
|
||||||
if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
|
// be shown for this case.
|
||||||
|
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.
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import android.annotation.IntDef;
|
|||||||
import android.annotation.NonNull;
|
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.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;
|
||||||
@@ -59,6 +61,7 @@ class PreAuthInfo {
|
|||||||
static final int CREDENTIAL_NOT_ENROLLED = 9;
|
static final int CREDENTIAL_NOT_ENROLLED = 9;
|
||||||
static final int BIOMETRIC_LOCKOUT_TIMED = 10;
|
static final int BIOMETRIC_LOCKOUT_TIMED = 10;
|
||||||
static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
|
static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
|
||||||
|
static final int BIOMETRIC_SENSOR_PRIVACY_ENABLED = 12;
|
||||||
@IntDef({AUTHENTICATOR_OK,
|
@IntDef({AUTHENTICATOR_OK,
|
||||||
BIOMETRIC_NO_HARDWARE,
|
BIOMETRIC_NO_HARDWARE,
|
||||||
BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
|
BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
|
||||||
@@ -69,7 +72,8 @@ class PreAuthInfo {
|
|||||||
BIOMETRIC_NOT_ENABLED_FOR_APPS,
|
BIOMETRIC_NOT_ENABLED_FOR_APPS,
|
||||||
CREDENTIAL_NOT_ENROLLED,
|
CREDENTIAL_NOT_ENROLLED,
|
||||||
BIOMETRIC_LOCKOUT_TIMED,
|
BIOMETRIC_LOCKOUT_TIMED,
|
||||||
BIOMETRIC_LOCKOUT_PERMANENT})
|
BIOMETRIC_LOCKOUT_PERMANENT,
|
||||||
|
BIOMETRIC_SENSOR_PRIVACY_ENABLED})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@interface AuthenticatorStatus {}
|
@interface AuthenticatorStatus {}
|
||||||
|
|
||||||
@@ -84,13 +88,15 @@ class PreAuthInfo {
|
|||||||
final boolean credentialAvailable;
|
final boolean credentialAvailable;
|
||||||
final boolean confirmationRequested;
|
final boolean confirmationRequested;
|
||||||
final boolean ignoreEnrollmentState;
|
final boolean ignoreEnrollmentState;
|
||||||
|
final int userId;
|
||||||
|
final Context context;
|
||||||
|
|
||||||
static PreAuthInfo create(ITrustManager trustManager,
|
static PreAuthInfo create(ITrustManager trustManager,
|
||||||
DevicePolicyManager devicePolicyManager,
|
DevicePolicyManager devicePolicyManager,
|
||||||
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)
|
boolean checkDevicePolicyManager, Context context)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
|
|
||||||
final boolean confirmationRequested = promptInfo.isConfirmationRequested();
|
final boolean confirmationRequested = promptInfo.isConfirmationRequested();
|
||||||
@@ -116,14 +122,22 @@ class PreAuthInfo {
|
|||||||
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
|
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
|
||||||
checkDevicePolicyManager, requestedStrength,
|
checkDevicePolicyManager, requestedStrength,
|
||||||
promptInfo.getAllowedSensorIds(),
|
promptInfo.getAllowedSensorIds(),
|
||||||
promptInfo.isIgnoreEnrollmentState());
|
promptInfo.isIgnoreEnrollmentState(),
|
||||||
|
context);
|
||||||
|
|
||||||
Slog.d(TAG, "Package: " + opPackageName
|
Slog.d(TAG, "Package: " + opPackageName
|
||||||
+ " Sensor ID: " + sensor.id
|
+ " Sensor ID: " + sensor.id
|
||||||
+ " Modality: " + sensor.modality
|
+ " Modality: " + sensor.modality
|
||||||
+ " Status: " + status);
|
+ " Status: " + status);
|
||||||
|
|
||||||
if (status == AUTHENTICATOR_OK) {
|
// A sensor with privacy enabled will still be eligible to
|
||||||
|
// authenticate with biometric prompt. This is so the framework can display
|
||||||
|
// a sensor privacy error message to users after briefly showing the
|
||||||
|
// Biometric Prompt.
|
||||||
|
//
|
||||||
|
// Note: if only a certain sensor is required and the privacy is enabled,
|
||||||
|
// canAuthenticate() will return false.
|
||||||
|
if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) {
|
||||||
eligibleSensors.add(sensor);
|
eligibleSensors.add(sensor);
|
||||||
} else {
|
} else {
|
||||||
ineligibleSensors.add(new Pair<>(sensor, status));
|
ineligibleSensors.add(new Pair<>(sensor, status));
|
||||||
@@ -133,7 +147,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());
|
promptInfo.isIgnoreEnrollmentState(), userId, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,7 +163,7 @@ 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) {
|
boolean ignoreEnrollmentState, Context context) {
|
||||||
|
|
||||||
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
|
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
|
||||||
return BIOMETRIC_NO_HARDWARE;
|
return BIOMETRIC_NO_HARDWARE;
|
||||||
@@ -175,6 +189,16 @@ 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 (sensorPrivacyManager
|
||||||
|
.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) {
|
||||||
|
return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
final @LockoutTracker.LockoutMode int lockoutMode =
|
final @LockoutTracker.LockoutMode int lockoutMode =
|
||||||
sensor.impl.getLockoutModeForUser(userId);
|
sensor.impl.getLockoutModeForUser(userId);
|
||||||
@@ -243,7 +267,8 @@ class PreAuthInfo {
|
|||||||
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) {
|
boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
|
||||||
|
Context context) {
|
||||||
mBiometricRequested = biometricRequested;
|
mBiometricRequested = biometricRequested;
|
||||||
mBiometricStrengthRequested = biometricStrengthRequested;
|
mBiometricStrengthRequested = biometricStrengthRequested;
|
||||||
this.credentialRequested = credentialRequested;
|
this.credentialRequested = credentialRequested;
|
||||||
@@ -253,6 +278,8 @@ class PreAuthInfo {
|
|||||||
this.credentialAvailable = credentialAvailable;
|
this.credentialAvailable = credentialAvailable;
|
||||||
this.confirmationRequested = confirmationRequested;
|
this.confirmationRequested = confirmationRequested;
|
||||||
this.ignoreEnrollmentState = ignoreEnrollmentState;
|
this.ignoreEnrollmentState = ignoreEnrollmentState;
|
||||||
|
this.userId = userId;
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
|
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
|
||||||
@@ -280,15 +307,35 @@ class PreAuthInfo {
|
|||||||
private Pair<Integer, Integer> getInternalStatus() {
|
private Pair<Integer, Integer> getInternalStatus() {
|
||||||
@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;
|
||||||
|
if (sensorPrivacyManager != null) {
|
||||||
|
cameraPrivacyEnabled = sensorPrivacyManager
|
||||||
|
.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId);
|
||||||
|
}
|
||||||
|
|
||||||
if (mBiometricRequested && credentialRequested) {
|
if (mBiometricRequested && credentialRequested) {
|
||||||
if (credentialAvailable || !eligibleSensors.isEmpty()) {
|
if (credentialAvailable || !eligibleSensors.isEmpty()) {
|
||||||
status = AUTHENTICATOR_OK;
|
|
||||||
if (credentialAvailable) {
|
|
||||||
modality |= TYPE_CREDENTIAL;
|
|
||||||
}
|
|
||||||
for (BiometricSensor sensor : eligibleSensors) {
|
for (BiometricSensor sensor : eligibleSensors) {
|
||||||
modality |= sensor.modality;
|
modality |= sensor.modality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (credentialAvailable) {
|
||||||
|
modality |= TYPE_CREDENTIAL;
|
||||||
|
status = AUTHENTICATOR_OK;
|
||||||
|
} else if (modality == TYPE_FACE && cameraPrivacyEnabled) {
|
||||||
|
// If the only modality requested is face, credential is unavailable,
|
||||||
|
// and the face sensor privacy is enabled then return
|
||||||
|
// BIOMETRIC_SENSOR_PRIVACY_ENABLED.
|
||||||
|
//
|
||||||
|
// Note: This sensor will still be eligible for calls to authenticate.
|
||||||
|
status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
|
||||||
|
} else {
|
||||||
|
status = AUTHENTICATOR_OK;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Pick the first sensor error if it exists
|
// Pick the first sensor error if it exists
|
||||||
if (!ineligibleSensors.isEmpty()) {
|
if (!ineligibleSensors.isEmpty()) {
|
||||||
@@ -302,10 +349,18 @@ class PreAuthInfo {
|
|||||||
}
|
}
|
||||||
} else if (mBiometricRequested) {
|
} else if (mBiometricRequested) {
|
||||||
if (!eligibleSensors.isEmpty()) {
|
if (!eligibleSensors.isEmpty()) {
|
||||||
status = AUTHENTICATOR_OK;
|
|
||||||
for (BiometricSensor sensor : eligibleSensors) {
|
for (BiometricSensor sensor : eligibleSensors) {
|
||||||
modality |= sensor.modality;
|
modality |= sensor.modality;
|
||||||
}
|
}
|
||||||
|
if (modality == TYPE_FACE && cameraPrivacyEnabled) {
|
||||||
|
// If the only modality requested is face and the privacy is enabled
|
||||||
|
// then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
|
||||||
|
//
|
||||||
|
// Note: This sensor will still be eligible for calls to authenticate.
|
||||||
|
status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
|
||||||
|
} else {
|
||||||
|
status = AUTHENTICATOR_OK;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Pick the first sensor error if it exists
|
// Pick the first sensor error if it exists
|
||||||
if (!ineligibleSensors.isEmpty()) {
|
if (!ineligibleSensors.isEmpty()) {
|
||||||
@@ -326,9 +381,9 @@ class PreAuthInfo {
|
|||||||
Slog.e(TAG, "No authenticators requested");
|
Slog.e(TAG, "No authenticators requested");
|
||||||
status = BIOMETRIC_NO_HARDWARE;
|
status = BIOMETRIC_NO_HARDWARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
|
Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
|
||||||
+ " AuthenticatorStatus: " + status);
|
+ " AuthenticatorStatus: " + status);
|
||||||
|
|
||||||
return new Pair<>(modality, status);
|
return new Pair<>(modality, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,6 +417,7 @@ class PreAuthInfo {
|
|||||||
case CREDENTIAL_NOT_ENROLLED:
|
case CREDENTIAL_NOT_ENROLLED:
|
||||||
case BIOMETRIC_LOCKOUT_TIMED:
|
case BIOMETRIC_LOCKOUT_TIMED:
|
||||||
case BIOMETRIC_LOCKOUT_PERMANENT:
|
case BIOMETRIC_LOCKOUT_PERMANENT:
|
||||||
|
case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
|
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_LOCKOUT_TIMED;
|
|||||||
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS;
|
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS;
|
||||||
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
|
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
|
||||||
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
|
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
|
||||||
|
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_SENSOR_PRIVACY_ENABLED;
|
||||||
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
|
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
@@ -278,6 +279,9 @@ public class Utils {
|
|||||||
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
|
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
|
||||||
biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
|
biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
|
||||||
break;
|
break;
|
||||||
|
case BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED:
|
||||||
|
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
|
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
|
||||||
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
|
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
|
||||||
@@ -337,7 +341,8 @@ public class Utils {
|
|||||||
|
|
||||||
case BIOMETRIC_LOCKOUT_PERMANENT:
|
case BIOMETRIC_LOCKOUT_PERMANENT:
|
||||||
return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
|
return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
|
||||||
|
case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
|
||||||
|
return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
|
||||||
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
|
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
|
||||||
case BIOMETRIC_HARDWARE_NOT_DETECTED:
|
case BIOMETRIC_HARDWARE_NOT_DETECTED:
|
||||||
case BIOMETRIC_NOT_ENABLED_FOR_APPS:
|
case BIOMETRIC_NOT_ENABLED_FOR_APPS:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
|
|||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
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.BiometricFaceConstants;
|
import android.hardware.biometrics.BiometricFaceConstants;
|
||||||
@@ -56,6 +57,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
|
|||||||
@NonNull private final LockoutCache mLockoutCache;
|
@NonNull private final LockoutCache mLockoutCache;
|
||||||
@Nullable private final NotificationManager mNotificationManager;
|
@Nullable private final NotificationManager mNotificationManager;
|
||||||
@Nullable private ICancellationSignal mCancellationSignal;
|
@Nullable private ICancellationSignal mCancellationSignal;
|
||||||
|
@Nullable private SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
|
||||||
private final int[] mBiometricPromptIgnoreList;
|
private final int[] mBiometricPromptIgnoreList;
|
||||||
private final int[] mBiometricPromptIgnoreListVendor;
|
private final int[] mBiometricPromptIgnoreListVendor;
|
||||||
@@ -81,6 +83,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
|
|||||||
mUsageStats = usageStats;
|
mUsageStats = usageStats;
|
||||||
mLockoutCache = lockoutCache;
|
mLockoutCache = lockoutCache;
|
||||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
mNotificationManager = context.getSystemService(NotificationManager.class);
|
||||||
|
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
|
||||||
|
|
||||||
final Resources resources = getContext().getResources();
|
final Resources resources = getContext().getResources();
|
||||||
mBiometricPromptIgnoreList = resources.getIntArray(
|
mBiometricPromptIgnoreList = resources.getIntArray(
|
||||||
@@ -108,7 +111,16 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
|
|||||||
@Override
|
@Override
|
||||||
protected void startHalOperation() {
|
protected void startHalOperation() {
|
||||||
try {
|
try {
|
||||||
|
if (mSensorPrivacyManager != null
|
||||||
|
&& mSensorPrivacyManager
|
||||||
|
.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
|
||||||
|
getTargetUserId())) {
|
||||||
|
onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
|
||||||
|
0 /* vendorCode */);
|
||||||
|
mCallback.onClientFinished(this, false /* success */);
|
||||||
|
} else {
|
||||||
mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
|
mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
|
||||||
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.e(TAG, "Remote exception when requesting auth", e);
|
Slog.e(TAG, "Remote exception when requesting auth", e);
|
||||||
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
|
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package com.android.server.biometrics.sensors.face.aidl;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.hardware.SensorPrivacyManager;
|
||||||
|
import android.hardware.biometrics.BiometricConstants;
|
||||||
import android.hardware.biometrics.BiometricsProtoEnums;
|
import android.hardware.biometrics.BiometricsProtoEnums;
|
||||||
import android.hardware.biometrics.common.ICancellationSignal;
|
import android.hardware.biometrics.common.ICancellationSignal;
|
||||||
import android.hardware.biometrics.face.ISession;
|
import android.hardware.biometrics.face.ISession;
|
||||||
@@ -41,6 +43,7 @@ public class FaceDetectClient extends AcquisitionClient<ISession> implements Det
|
|||||||
|
|
||||||
private final boolean mIsStrongBiometric;
|
private final boolean mIsStrongBiometric;
|
||||||
@Nullable private ICancellationSignal mCancellationSignal;
|
@Nullable private ICancellationSignal mCancellationSignal;
|
||||||
|
@Nullable private SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
|
||||||
public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
|
public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
|
||||||
@NonNull IBinder token, long requestId,
|
@NonNull IBinder token, long requestId,
|
||||||
@@ -51,6 +54,7 @@ public class FaceDetectClient extends AcquisitionClient<ISession> implements Det
|
|||||||
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
|
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
|
||||||
setRequestId(requestId);
|
setRequestId(requestId);
|
||||||
mIsStrongBiometric = isStrongBiometric;
|
mIsStrongBiometric = isStrongBiometric;
|
||||||
|
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,6 +77,14 @@ public class FaceDetectClient extends AcquisitionClient<ISession> implements Det
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startHalOperation() {
|
protected void startHalOperation() {
|
||||||
|
if (mSensorPrivacyManager != null
|
||||||
|
&& mSensorPrivacyManager
|
||||||
|
.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, getTargetUserId())) {
|
||||||
|
onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
|
||||||
|
mCallback.onClientFinished(this, false /* success */);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mCancellationSignal = getFreshDaemon().detectInteraction();
|
mCancellationSignal = getFreshDaemon().detectInteraction();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.face.hidl;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
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.BiometricFaceConstants;
|
import android.hardware.biometrics.BiometricFaceConstants;
|
||||||
@@ -55,6 +56,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
|
|||||||
private final int[] mKeyguardIgnoreListVendor;
|
private final int[] mKeyguardIgnoreListVendor;
|
||||||
|
|
||||||
private int mLastAcquire;
|
private int mLastAcquire;
|
||||||
|
private SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
|
||||||
FaceAuthenticationClient(@NonNull Context context,
|
FaceAuthenticationClient(@NonNull Context context,
|
||||||
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
|
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
|
||||||
@@ -71,6 +73,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
|
|||||||
isKeyguardBypassEnabled);
|
isKeyguardBypassEnabled);
|
||||||
setRequestId(requestId);
|
setRequestId(requestId);
|
||||||
mUsageStats = usageStats;
|
mUsageStats = usageStats;
|
||||||
|
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
|
||||||
|
|
||||||
final Resources resources = getContext().getResources();
|
final Resources resources = getContext().getResources();
|
||||||
mBiometricPromptIgnoreList = resources.getIntArray(
|
mBiometricPromptIgnoreList = resources.getIntArray(
|
||||||
@@ -97,6 +100,15 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startHalOperation() {
|
protected void startHalOperation() {
|
||||||
|
|
||||||
|
if (mSensorPrivacyManager != null
|
||||||
|
&& mSensorPrivacyManager
|
||||||
|
.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, getTargetUserId())) {
|
||||||
|
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
|
||||||
|
mCallback.onClientFinished(this, false /* success */);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
getFreshDaemon().authenticate(mOperationId);
|
getFreshDaemon().authenticate(mOperationId);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
|||||||
@@ -302,6 +302,65 @@ public class AuthSessionTest {
|
|||||||
testInvokesCancel(session -> session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null));
|
testInvokesCancel(session -> session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (b/208484275) : Enable these tests
|
||||||
|
// @Test
|
||||||
|
// public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
|
||||||
|
// SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
|
||||||
|
// when(manager
|
||||||
|
// .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
|
||||||
|
// .thenReturn(false);
|
||||||
|
// when(mContext.getSystemService(SensorPrivacyManager.class))
|
||||||
|
// .thenReturn(manager);
|
||||||
|
// setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
|
||||||
|
// mock(IBiometricAuthenticator.class));
|
||||||
|
// final PromptInfo promptInfo = createPromptInfo(Authenticators.BIOMETRIC_STRONG);
|
||||||
|
// final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
|
||||||
|
// assertEquals(BiometricManager.BIOMETRIC_SUCCESS, preAuthInfo.getCanAuthenticateResult());
|
||||||
|
// for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
|
||||||
|
// assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// public void testPreAuth_cannotAuthAndPrivacyEnabled() throws Exception {
|
||||||
|
// SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
|
||||||
|
// when(manager
|
||||||
|
// .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
|
||||||
|
// .thenReturn(true);
|
||||||
|
// when(mContext.getSystemService(SensorPrivacyManager.class))
|
||||||
|
// .thenReturn(manager);
|
||||||
|
// setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
|
||||||
|
// mock(IBiometricAuthenticator.class));
|
||||||
|
// final PromptInfo promptInfo = createPromptInfo(Authenticators.BIOMETRIC_STRONG);
|
||||||
|
// final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
|
||||||
|
// assertEquals(BiometricManager.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED,
|
||||||
|
// preAuthInfo.getCanAuthenticateResult());
|
||||||
|
// // Even though canAuth returns privacy enabled, we should still be able to authenticate.
|
||||||
|
// for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
|
||||||
|
// assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// public void testPreAuth_canAuthAndPrivacyEnabledCredentialEnabled() throws Exception {
|
||||||
|
// SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
|
||||||
|
// when(manager
|
||||||
|
// .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
|
||||||
|
// .thenReturn(true);
|
||||||
|
// when(mContext.getSystemService(SensorPrivacyManager.class))
|
||||||
|
// .thenReturn(manager);
|
||||||
|
// setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
|
||||||
|
// mock(IBiometricAuthenticator.class));
|
||||||
|
// final PromptInfo promptInfo =
|
||||||
|
// createPromptInfo(Authenticators.BIOMETRIC_STRONG
|
||||||
|
// | Authenticators. DEVICE_CREDENTIAL);
|
||||||
|
// final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
|
||||||
|
// assertEquals(BiometricManager.BIOMETRIC_SUCCESS, preAuthInfo.getCanAuthenticateResult());
|
||||||
|
// for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
|
||||||
|
// assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
private void testInvokesCancel(Consumer<AuthSession> sessionConsumer) throws RemoteException {
|
private void testInvokesCancel(Consumer<AuthSession> sessionConsumer) throws RemoteException {
|
||||||
final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
|
final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
|
||||||
|
|
||||||
@@ -331,7 +390,8 @@ public class AuthSessionTest {
|
|||||||
userId,
|
userId,
|
||||||
promptInfo,
|
promptInfo,
|
||||||
TEST_PACKAGE,
|
TEST_PACKAGE,
|
||||||
checkDevicePolicyManager);
|
checkDevicePolicyManager,
|
||||||
|
mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthSession createAuthSession(List<BiometricSensor> sensors,
|
private AuthSession createAuthSession(List<BiometricSensor> sensors,
|
||||||
|
|||||||
Reference in New Issue
Block a user