1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.biometrics.sensors.face.hidl; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.hardware.biometrics.face.V1_0.IBiometricsFace; 22 import android.hardware.face.IFaceServiceReceiver; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.util.Slog; 26 27 import com.android.internal.util.Preconditions; 28 import com.android.server.biometrics.log.BiometricContext; 29 import com.android.server.biometrics.log.BiometricLogger; 30 import com.android.server.biometrics.sensors.ClientMonitorCallback; 31 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; 32 import com.android.server.biometrics.sensors.GenerateChallengeClient; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.function.Supplier; 37 38 /** 39 * Face-specific generateChallenge client supporting the 40 * {@link android.hardware.biometrics.face.V1_0} HIDL interface. 41 */ 42 public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> { 43 44 private static final String TAG = "FaceGenerateChallengeClient"; 45 static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes 46 private static final ClientMonitorCallback EMPTY_CALLBACK = new ClientMonitorCallback() { 47 }; 48 49 private final long mCreatedAt; 50 private List<IFaceServiceReceiver> mWaiting; 51 private Long mChallengeResult; 52 FaceGenerateChallengeClient(@onNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, long now)53 FaceGenerateChallengeClient(@NonNull Context context, 54 @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, 55 @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, 56 int sensorId, @NonNull BiometricLogger logger, 57 @NonNull BiometricContext biometricContext, long now) { 58 super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger, 59 biometricContext); 60 mCreatedAt = now; 61 mWaiting = new ArrayList<>(); 62 } 63 64 @Override startHalOperation()65 protected void startHalOperation() { 66 mChallengeResult = null; 67 try { 68 mChallengeResult = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value; 69 // send the result to the original caller via mCallback and any waiting callers 70 // that called reuseResult 71 sendChallengeResult(getListener(), mCallback); 72 for (IFaceServiceReceiver receiver : mWaiting) { 73 sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK); 74 } 75 } catch (RemoteException e) { 76 Slog.e(TAG, "generateChallenge failed", e); 77 mCallback.onClientFinished(this, false /* success */); 78 } finally { 79 mWaiting = null; 80 } 81 } 82 83 /** @return An arbitrary time value for caching provided to the constructor. */ getCreatedAt()84 public long getCreatedAt() { 85 return mCreatedAt; 86 } 87 88 /** 89 * Reuse the result of this operation when it is available. The receiver will be notified 90 * immediately if a challenge has already been generated. 91 * 92 * @param receiver receiver to be notified of challenge result 93 */ reuseResult(@onNull IFaceServiceReceiver receiver)94 public void reuseResult(@NonNull IFaceServiceReceiver receiver) { 95 if (mWaiting != null) { 96 mWaiting.add(receiver); 97 } else { 98 sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK); 99 } 100 } 101 sendChallengeResult(@onNull ClientMonitorCallbackConverter receiver, @NonNull ClientMonitorCallback ownerCallback)102 private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver, 103 @NonNull ClientMonitorCallback ownerCallback) { 104 Preconditions.checkState(mChallengeResult != null, "result not available"); 105 try { 106 receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult); 107 ownerCallback.onClientFinished(this, true /* success */); 108 } catch (RemoteException e) { 109 Slog.e(TAG, "Remote exception", e); 110 ownerCallback.onClientFinished(this, false /* success */); 111 } 112 } 113 } 114