5/n: Add AuthenticationClient for IFingerprint
1) Adds FingerprintAuthenticationClient 2) Finishes plumbing for cancel enrollment Bug: 170497736 Test: Builds Change-Id: I4c53face501e16af5cf781c757c363e5dd30ae3c
This commit is contained in:
@@ -89,7 +89,8 @@ public final class ClientMonitorCallbackConverter {
|
||||
}
|
||||
}
|
||||
|
||||
void onError(int sensorId, int cookie, int error, int vendorCode) throws RemoteException {
|
||||
public void onError(int sensorId, int cookie, int error, int vendorCode)
|
||||
throws RemoteException {
|
||||
if (mSensorReceiver != null) {
|
||||
mSensorReceiver.onError(sensorId, cookie, error, vendorCode);
|
||||
} else if (mFaceServiceReceiver != null) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface that clients interested/eligible for lockout events should implement.
|
||||
*/
|
||||
public interface LockoutConsumer {
|
||||
void onLockoutTimed(long durationMillis);
|
||||
void onLockoutPermanent();
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.annotation.Nullable;
|
||||
import android.app.TaskStackListener;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.hardware.biometrics.BiometricFingerprintConstants;
|
||||
import android.hardware.biometrics.BiometricsProtoEnums;
|
||||
import android.hardware.biometrics.common.ICancellationSignal;
|
||||
import android.hardware.biometrics.fingerprint.ISession;
|
||||
import android.hardware.fingerprint.IUdfpsOverlayController;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.biometrics.sensors.AuthenticationClient;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
|
||||
import com.android.server.biometrics.sensors.LockoutConsumer;
|
||||
import com.android.server.biometrics.sensors.LockoutTracker;
|
||||
import com.android.server.biometrics.sensors.fingerprint.Udfps;
|
||||
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Fingerprint-specific authentication client supporting the
|
||||
* {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
|
||||
*/
|
||||
public class FingerprintAuthenticationClient extends AuthenticationClient<ISession> implements
|
||||
Udfps, LockoutConsumer {
|
||||
private static final String TAG = "FingerprintAuthenticationClient";
|
||||
|
||||
@NonNull private final LockoutCache mLockoutCache;
|
||||
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
|
||||
@Nullable private ICancellationSignal mCancellationSignal;
|
||||
|
||||
public FingerprintAuthenticationClient(@NonNull Context context,
|
||||
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
|
||||
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
|
||||
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
|
||||
int sensorId, boolean isStrongBiometric, int statsClient,
|
||||
@Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
|
||||
@Nullable IUdfpsOverlayController udfpsOverlayController) {
|
||||
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
|
||||
cookie, requireConfirmation, sensorId, isStrongBiometric,
|
||||
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
|
||||
lockoutCache);
|
||||
mLockoutCache = lockoutCache;
|
||||
mUdfpsOverlayController = udfpsOverlayController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
|
||||
boolean authenticated, ArrayList<Byte> token) {
|
||||
super.onAuthenticated(identifier, authenticated, token);
|
||||
|
||||
if (authenticated) {
|
||||
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
|
||||
mCallback.onClientFinished(this, true /* success */);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startHalOperation() {
|
||||
UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
|
||||
try {
|
||||
mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
|
||||
0 /* vendorCode */);
|
||||
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
|
||||
mCallback.onClientFinished(this, false /* success */);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopHalOperation() {
|
||||
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
|
||||
try {
|
||||
mCancellationSignal.cancel();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
|
||||
0 /* vendorCode */);
|
||||
mCallback.onClientFinished(this, false /* success */);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerDown(int x, int y, float minor, float major) {
|
||||
try {
|
||||
getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerUp() {
|
||||
try {
|
||||
getFreshDaemon().onPointerUp(0 /* pointerId */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLockoutTimed(long durationMillis) {
|
||||
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
|
||||
// Lockout metrics are logged as an error code.
|
||||
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
|
||||
logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
|
||||
|
||||
try {
|
||||
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLockoutPermanent() {
|
||||
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
|
||||
// Lockout metrics are logged as an error code.
|
||||
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
|
||||
logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
|
||||
|
||||
try {
|
||||
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.IActivityTaskManager;
|
||||
import android.app.TaskStackListener;
|
||||
import android.content.Context;
|
||||
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;
|
||||
@@ -37,12 +39,15 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.server.biometrics.Utils;
|
||||
import com.android.server.biometrics.sensors.AuthenticationClient;
|
||||
import com.android.server.biometrics.sensors.ClientMonitor;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
|
||||
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
|
||||
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
|
||||
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
|
||||
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
|
||||
import com.android.server.biometrics.sensors.fingerprint.Udfps;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -52,6 +57,7 @@ import java.util.List;
|
||||
/**
|
||||
* Provider for a single instance of the {@link IFingerprint} HAL.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
@@ -60,9 +66,48 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
@NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
|
||||
@NonNull private final Handler mHandler;
|
||||
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
|
||||
@NonNull private final IActivityTaskManager mActivityTaskManager;
|
||||
@NonNull private final BiometricTaskStackListener mTaskStackListener;
|
||||
|
||||
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
|
||||
|
||||
private final class BiometricTaskStackListener extends TaskStackListener {
|
||||
@Override
|
||||
public void onTaskStackChanged() {
|
||||
mHandler.post(() -> {
|
||||
for (int i = 0; i < mSensors.size(); i++) {
|
||||
final ClientMonitor<?> client = mSensors.get(i).getScheduler()
|
||||
.getCurrentClient();
|
||||
if (!(client instanceof AuthenticationClient)) {
|
||||
Slog.e(getTag(), "Task stack changed for client: " + client);
|
||||
continue;
|
||||
}
|
||||
if (Utils.isKeyguard(mContext, client.getOwnerString())) {
|
||||
continue; // Keyguard is always allowed
|
||||
}
|
||||
|
||||
try {
|
||||
final List<ActivityManager.RunningTaskInfo> runningTasks =
|
||||
mActivityTaskManager.getTasks(1);
|
||||
if (!runningTasks.isEmpty()) {
|
||||
final String topPackage =
|
||||
runningTasks.get(0).topActivity.getPackageName();
|
||||
if (!topPackage.contentEquals(client.getOwnerString())
|
||||
&& !client.isAlreadyDone()) {
|
||||
Slog.e(getTag(), "Stopping background authentication, top: "
|
||||
+ topPackage + " currentClient: " + client);
|
||||
mSensors.get(i).getScheduler()
|
||||
.cancelAuthentication(client.getToken());
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(getTag(), "Unable to get running tasks", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props,
|
||||
@NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
|
||||
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
@@ -72,6 +117,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
mLazyDaemon = this::getHalInstance;
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mLockoutResetDispatcher = lockoutResetDispatcher;
|
||||
mActivityTaskManager = ActivityTaskManager.getService();
|
||||
mTaskStackListener = new BiometricTaskStackListener();
|
||||
|
||||
for (SensorProps prop : props) {
|
||||
final int sensorId = prop.commonProps.sensorId;
|
||||
@@ -135,7 +182,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
|
||||
}
|
||||
|
||||
private void scheduleCreateSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId,
|
||||
private void createNewSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId,
|
||||
int userId) throws RemoteException {
|
||||
// Note that per IFingerprint createSession contract, this method will block until all
|
||||
// existing operations are canceled/finished. However, also note that this is fine, since
|
||||
@@ -178,7 +225,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
|
||||
try {
|
||||
if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
|
||||
scheduleCreateSessionWithoutHandler(daemon, sensorId, userId);
|
||||
createNewSessionWithoutHandler(daemon, sensorId, userId);
|
||||
}
|
||||
|
||||
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
|
||||
@@ -222,19 +269,15 @@ 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);
|
||||
}
|
||||
// If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
|
||||
// this operation. We should not send the callback yet, since the scheduler may
|
||||
// be processing something else.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
|
||||
scheduleCreateSessionWithoutHandler(daemon, sensorId, userId);
|
||||
createNewSessionWithoutHandler(daemon, sensorId, userId);
|
||||
}
|
||||
|
||||
final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
|
||||
@@ -262,7 +305,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
|
||||
@Override
|
||||
public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
|
||||
|
||||
mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -277,17 +320,43 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
|
||||
@NonNull String opPackageName, boolean restricted, int statsClient,
|
||||
boolean isKeyguard) {
|
||||
mHandler.post(() -> {
|
||||
final IFingerprint daemon = getHalInstance();
|
||||
if (daemon == null) {
|
||||
Slog.e(getTag(), "Null daemon during authenticate, sensorId: " + sensorId);
|
||||
// If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
|
||||
// this operation. We should not send the callback yet, since the scheduler may
|
||||
// be processing something else.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
|
||||
createNewSessionWithoutHandler(daemon, sensorId, userId);
|
||||
}
|
||||
|
||||
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
|
||||
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
|
||||
mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
|
||||
operationId, restricted, opPackageName, cookie,
|
||||
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
|
||||
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
|
||||
mUdfpsOverlayController);
|
||||
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(getTag(), "Remote exception when scheduling authenticate", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPreparedClient(int sensorId, int cookie) {
|
||||
|
||||
mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
|
||||
|
||||
mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelAuthentication(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -320,7 +389,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
|
||||
@Override
|
||||
public int getLockoutModeForUser(int sensorId, int userId) {
|
||||
return 0;
|
||||
return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -330,12 +399,24 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
|
||||
@Override
|
||||
public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
|
||||
|
||||
final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
|
||||
if (!(client instanceof Udfps)) {
|
||||
Slog.e(getTag(), "onPointerDown received during client: " + client);
|
||||
return;
|
||||
}
|
||||
final Udfps udfps = (Udfps) client;
|
||||
udfps.onPointerDown(x, y, minor, major);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerUp(int sensorId) {
|
||||
|
||||
final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
|
||||
if (!(client instanceof Udfps)) {
|
||||
Slog.e(getTag(), "onPointerUp received during client: " + client);
|
||||
return;
|
||||
}
|
||||
final Udfps udfps = (Udfps) client;
|
||||
udfps.onPointerUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.server.biometrics.sensors.AuthenticationConsumer;
|
||||
import com.android.server.biometrics.sensors.BiometricScheduler;
|
||||
import com.android.server.biometrics.sensors.ClientMonitor;
|
||||
import com.android.server.biometrics.sensors.Interruptable;
|
||||
import com.android.server.biometrics.sensors.LockoutConsumer;
|
||||
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
|
||||
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
|
||||
|
||||
@@ -209,12 +210,32 @@ class Sensor {
|
||||
|
||||
@Override
|
||||
public void onLockoutTimed(long durationMillis) {
|
||||
mHandler.post(() -> {
|
||||
final ClientMonitor<?> client = mScheduler.getCurrentClient();
|
||||
if (!(client instanceof LockoutConsumer)) {
|
||||
Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
|
||||
+ Utils.getClientName(client));
|
||||
return;
|
||||
}
|
||||
|
||||
final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
|
||||
lockoutConsumer.onLockoutTimed(durationMillis);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLockoutPermanent() {
|
||||
mHandler.post(() -> {
|
||||
final ClientMonitor<?> client = mScheduler.getCurrentClient();
|
||||
if (!(client instanceof LockoutConsumer)) {
|
||||
Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
|
||||
+ Utils.getClientName(client));
|
||||
return;
|
||||
}
|
||||
|
||||
final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
|
||||
lockoutConsumer.onLockoutPermanent();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -594,16 +594,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
|
||||
|
||||
@Override
|
||||
public void startPreparedClient(int sensorId, int cookie) {
|
||||
mHandler.post(() -> {
|
||||
mScheduler.startPreparedClient(cookie);
|
||||
});
|
||||
mHandler.post(() -> mScheduler.startPreparedClient(cookie));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
|
||||
mHandler.post(() -> {
|
||||
mScheduler.cancelAuthentication(token);
|
||||
});
|
||||
mHandler.post(() -> mScheduler.cancelAuthentication(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user