Merge "4/n: Add RevokeChallenge and ResetLockout for IFingerprint"

This commit is contained in:
Kevin Chyn
2020-10-12 20:30:09 +00:00
committed by Android (Google) Code Review
11 changed files with 266 additions and 12 deletions

View File

@@ -636,13 +636,24 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
* Finishes enrollment and cancels the current auth token.
* Revokes the current challenge.
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void revokeChallenge() {
// On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1,
// this parameter is ignored.
revokeChallenge(0L);
}
/**
* Revokes the specified challenge.
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void revokeChallenge(long challenge) {
if (mService != null) try {
mService.revokeChallenge(mToken, mContext.getOpPackageName());
mService.revokeChallenge(mToken, mContext.getOpPackageName(), challenge);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -96,7 +96,7 @@ interface IFingerprintService {
void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName);
// Finish an enrollment sequence and invalidate the authentication token
void revokeChallenge(IBinder token, String opPackageName);
void revokeChallenge(IBinder token, String opPackageName, long challenge);
// Determine if a user has at least one enrolled fingerprint
boolean hasEnrolledFingerprints(int userId, String opPackageName);

View File

@@ -187,7 +187,7 @@ public class FingerprintService extends SystemService {
}
@Override // Binder call
public void revokeChallenge(IBinder token, String opPackageName) {
public void revokeChallenge(IBinder token, String opPackageName, long challenge) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -196,7 +196,8 @@ public class FingerprintService extends SystemService {
return;
}
provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName);
provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName,
challenge);
}
@Override // Binder call

View File

@@ -68,7 +68,7 @@ public interface ServiceProvider {
@NonNull IFingerprintServiceReceiver receiver, String opPackageName);
void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
@NonNull String opPackageName);
@NonNull String opPackageName, long challenge);
void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,

View File

@@ -34,7 +34,7 @@ public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<
private static final String TAG = "FingerprintGenerateChallengeClient";
private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
private IGenerateChallengeCallback mGenerateChallengeCallback =
private final IGenerateChallengeCallback mGenerateChallengeCallback =
new IGenerateChallengeCallback.Stub() {
@Override
public void onChallengeGenerated(int sensorId, int userId, long challenge) {

View File

@@ -24,6 +24,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -58,6 +59,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
@NonNull private final Handler mHandler;
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@@ -69,6 +71,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mSensors = new SparseArray<>();
mLazyDaemon = this::getHalInstance;
mHandler = new Handler(Looper.getMainLooper());
mLockoutResetDispatcher = lockoutResetDispatcher;
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -157,7 +160,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull
@Override
public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
for (int i = 0; i < mSensors.size(); i++) {
props.add(mSensors.valueAt(i).getSensorProperties());
}
@@ -166,7 +169,27 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
Slog.e(getTag(), "Null daemon during resetLockout, sensorId: " + sensorId);
return;
}
try {
if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
scheduleCreateSessionWithoutHandler(daemon, sensorId, userId);
}
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when scheduling resetLockout", e);
}
});
}
@Override
@@ -176,14 +199,19 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
scheduleForSensor(sensorId, client);
});
}
@Override
public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
@NonNull String opPackageName) {
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client =
new FingerprintRevokeChallengeClient(mContext, mLazyDaemon, token,
opPackageName, sensorId, challenge);
scheduleForSensor(sensorId, client);
});
}
@Override
@@ -194,6 +222,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId);
try {
receiver.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
} catch (RemoteException e) {
Slog.e(getTag(), "Unable to send HW_UNAVAILABLE", e);
}
return;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 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.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
* Fingerprint-specific resetLockout client for the {@link IFingerprint} AIDL HAL interface.
* Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
* cleared.
*/
public class FingerprintResetLockoutClient extends ClientMonitor<ISession> {
private static final String TAG = "FingerprintResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
private final LockoutCache mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
public FingerprintResetLockoutClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
}
@Override
public void unableToStart() {
// Nothing to do here
}
@Override
protected void startHalOperation() {
try {
getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
mCallback.onClientFinished(this, false /* success */);
}
}
void onLockoutCleared() {
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
mCallback.onClientFinished(this, true /* success */);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2020 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.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.IRevokeChallengeCallback;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
/**
* Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
*/
public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<IFingerprint> {
private static final String TAG = "FingerpirntRevokeChallengeClient";
private final long mChallenge;
private final IRevokeChallengeCallback mRevokeChallengeCallback =
new IRevokeChallengeCallback.Stub() {
@Override
public void onChallengeRevoked(int sensorId, int userId, long challenge) {
final boolean success = challenge == mChallenge;
mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
}
};
public FingerprintRevokeChallengeClient(
@NonNull Context context,
@NonNull LazyDaemon<IFingerprint> lazyDaemon,
@NonNull IBinder token,
@NonNull String owner, int sensorId, long challenge) {
super(context, lazyDaemon, token, owner, sensorId);
mChallenge = challenge;
}
@Override
protected void startHalOperation() {
try {
getFreshDaemon().revokeChallenge(getSensorId(), getTargetUserId(), mChallenge,
mRevokeChallengeCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to revokeChallenge", e);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2020 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.sensors.fingerprint.aidl;
import android.util.SparseIntArray;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
* For a single sensor, caches lockout states for all users.
*/
public class LockoutCache implements LockoutTracker {
// Map of userId to LockoutMode
private final SparseIntArray mUserLockoutStates;
LockoutCache() {
mUserLockoutStates = new SparseIntArray();
}
public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
synchronized (this) {
mUserLockoutStates.put(userId, mode);
}
}
@Override
public int getLockoutModeForUser(int userId) {
synchronized (this) {
return mUserLockoutStates.get(userId, LOCKOUT_NONE);
}
}
}

View File

@@ -54,6 +54,7 @@ class Sensor {
@NonNull private final Handler mHandler;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
@NonNull private final BiometricScheduler mScheduler;
@NonNull private final LockoutCache mLockoutCache;
@Nullable private Session mCurrentSession; // TODO: Death recipient
@NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
@@ -82,6 +83,7 @@ class Sensor {
mHandler = handler;
mSensorProperties = sensorProperties;
mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
mLockoutCache = new LockoutCache();
mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
}
@@ -217,7 +219,18 @@ class Sensor {
@Override
public void onLockoutCleared() {
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintResetLockoutClient)) {
Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
+ Utils.getClientName(client));
return;
}
final FingerprintResetLockoutClient resetLockoutClient =
(FingerprintResetLockoutClient) client;
resetLockoutClient.onLockoutCleared();
});
}
@Override
@@ -253,4 +266,8 @@ class Sensor {
@NonNull BiometricScheduler getScheduler() {
return mScheduler;
}
@NonNull LockoutCache getLockoutCache() {
return mLockoutCache;
}
}

View File

@@ -518,7 +518,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Override
public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
@NonNull String opPackageName) {
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);