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; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.hardware.biometrics.BiometricAuthenticator; 22 import android.os.Build; 23 import android.os.IBinder; 24 import android.util.Slog; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.server.biometrics.BiometricsProto; 28 import com.android.server.biometrics.log.BiometricContext; 29 import com.android.server.biometrics.log.BiometricLogger; 30 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.function.Supplier; 35 36 /** 37 * Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the 38 * internal states when cleaning up mismatch between framework and HAL templates. This client 39 * ends either when 40 * 1) The HAL and Framework are in sync, and 41 * {@link #onEnumerationResult(BiometricAuthenticator.Identifier, int)} returns true, or 42 * 2) The HAL and Framework are not in sync, and 43 * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/ 44 */ 45 public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T> 46 extends HalClientMonitor<T> implements EnumerateConsumer, RemovalConsumer, 47 EnrollmentModifier { 48 49 private static final String TAG = "Biometrics/InternalCleanupClient"; 50 51 /** 52 * Container for enumerated templates. Used to keep track when cleaning up unknown 53 * templates. 54 */ 55 private static final class UserTemplate { 56 final BiometricAuthenticator.Identifier mIdentifier; 57 final int mUserId; UserTemplate(BiometricAuthenticator.Identifier identifier, int userId)58 UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) { 59 this.mIdentifier = identifier; 60 this.mUserId = userId; 61 } 62 } 63 64 private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>(); 65 private final BiometricUtils<S> mBiometricUtils; 66 private final Map<Integer, Long> mAuthenticatorIds; 67 private final boolean mHasEnrollmentsBeforeStarting; 68 private BaseClientMonitor mCurrentTask; 69 private boolean mFavorHalEnrollments = false; 70 71 private final ClientMonitorCallback mEnumerateCallback = new ClientMonitorCallback() { 72 @Override 73 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { 74 final List<BiometricAuthenticator.Identifier> unknownHALTemplates = 75 ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates(); 76 77 Slog.d(TAG, "Enumerate onClientFinished: " + clientMonitor + ", success: " + success); 78 79 if (!unknownHALTemplates.isEmpty()) { 80 Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion"); 81 } 82 for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) { 83 mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate, 84 mCurrentTask.getTargetUserId())); 85 } 86 87 if (mUnknownHALTemplates.isEmpty()) { 88 // No unknown HAL templates. Unknown framework templates are already cleaned up in 89 // InternalEnumerateClient. Finish this client. 90 mCallback.onClientFinished(InternalCleanupClient.this, success); 91 } else { 92 if (mFavorHalEnrollments && Build.isDebuggable()) { 93 // on debug builds, optionally allow the HAL be the source of 94 // truth for enrollments 95 try { 96 for (UserTemplate template : mUnknownHALTemplates) { 97 Slog.i(TAG, "Adding unknown HAL template: " 98 + template.mIdentifier.getBiometricId()); 99 onAddUnknownTemplate(template.mUserId, template.mIdentifier); 100 } 101 } finally { 102 mCallback.onClientFinished(InternalCleanupClient.this, success); 103 } 104 } else { 105 startCleanupUnknownHalTemplates(); 106 } 107 } 108 } 109 }; 110 111 private final ClientMonitorCallback mRemoveCallback = new ClientMonitorCallback() { 112 @Override 113 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { 114 Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success); 115 if (mUnknownHALTemplates.isEmpty()) { 116 mCallback.onClientFinished(InternalCleanupClient.this, success); 117 } else { 118 startCleanupUnknownHalTemplates(); 119 } 120 } 121 }; 122 getEnumerateClient(Context context, Supplier<T> lazyDaemon, IBinder token, int userId, String owner, List<S> enrolledList, BiometricUtils<S> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext)123 protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, 124 Supplier<T> lazyDaemon, IBinder token, int userId, String owner, 125 List<S> enrolledList, BiometricUtils<S> utils, int sensorId, 126 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext); 127 getRemovalClient(Context context, Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils<S> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds)128 protected abstract RemovalClient<S, T> getRemovalClient(Context context, 129 Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner, 130 BiometricUtils<S> utils, int sensorId, 131 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, 132 Map<Integer, Long> authenticatorIds); 133 InternalCleanupClient(@onNull Context context, @NonNull Supplier<T> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull BiometricUtils<S> utils, @NonNull Map<Integer, Long> authenticatorIds)134 protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, 135 int userId, @NonNull String owner, int sensorId, 136 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, 137 @NonNull BiometricUtils<S> utils, 138 @NonNull Map<Integer, Long> authenticatorIds) { 139 super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, 140 userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); 141 mBiometricUtils = utils; 142 mAuthenticatorIds = authenticatorIds; 143 mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty(); 144 } 145 startCleanupUnknownHalTemplates()146 private void startCleanupUnknownHalTemplates() { 147 Slog.d(TAG, "startCleanupUnknownHalTemplates, size: " + mUnknownHALTemplates.size()); 148 149 UserTemplate template = mUnknownHALTemplates.get(0); 150 mUnknownHALTemplates.remove(template); 151 mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(), 152 template.mIdentifier.getBiometricId(), template.mUserId, 153 getContext().getPackageName(), mBiometricUtils, getSensorId(), 154 getLogger(), getBiometricContext(), mAuthenticatorIds); 155 156 getLogger().logUnknownEnrollmentInHal(); 157 158 mCurrentTask.start(mRemoveCallback); 159 } 160 161 @Override unableToStart()162 public void unableToStart() { 163 // nothing to do here 164 } 165 166 @Override start(@onNull ClientMonitorCallback callback)167 public void start(@NonNull ClientMonitorCallback callback) { 168 super.start(callback); 169 170 final List<S> enrolledList = 171 mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()); 172 173 // Start enumeration. Removal will start if necessary, when enumeration is completed. 174 mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(), 175 getOwnerString(), enrolledList, mBiometricUtils, getSensorId(), getLogger(), 176 getBiometricContext()); 177 178 Slog.d(TAG, "Starting enumerate: " + mCurrentTask + " enrolledList size:" 179 + enrolledList.size()); 180 mCurrentTask.start(mEnumerateCallback); 181 } 182 183 @Override startHalOperation()184 protected void startHalOperation() { 185 // Internal cleanup's start method does not require a HAL operation, but rather 186 // relies on its subtask's ClientMonitor to start the proper HAL operation. 187 } 188 189 @Override onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)190 public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { 191 if (!(mCurrentTask instanceof RemovalClient)) { 192 Slog.e(TAG, "onRemoved received during client: " 193 + mCurrentTask.getClass().getSimpleName()); 194 return; 195 } 196 ((RemovalClient<S, T>) mCurrentTask).onRemoved(identifier, remaining); 197 } 198 199 @Override hasEnrollmentStateChanged()200 public boolean hasEnrollmentStateChanged() { 201 final boolean hasEnrollmentsNow = !mBiometricUtils 202 .getBiometricsForUser(getContext(), getTargetUserId()).isEmpty(); 203 return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting; 204 } 205 206 @Override hasEnrollments()207 public boolean hasEnrollments() { 208 return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty(); 209 } 210 211 @Override onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining)212 public void onEnumerationResult(BiometricAuthenticator.Identifier identifier, 213 int remaining) { 214 if (!(mCurrentTask instanceof InternalEnumerateClient)) { 215 Slog.e(TAG, "onEnumerationResult received during client: " 216 + mCurrentTask.getClass().getSimpleName()); 217 return; 218 } 219 Slog.d(TAG, "onEnumerated, remaining: " + remaining); 220 ((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining); 221 } 222 223 /** When set unknown templates in the HAL will be added instead of deleted. */ setFavorHalEnrollments()224 public void setFavorHalEnrollments() { 225 mFavorHalEnrollments = true; 226 } 227 228 /** Called when an unknown template is found and setFavorHalEnrollments was requested. */ onAddUnknownTemplate(int userId, @NonNull BiometricAuthenticator.Identifier identifier)229 protected void onAddUnknownTemplate(int userId, 230 @NonNull BiometricAuthenticator.Identifier identifier) {} 231 232 @Override getProtoEnum()233 public int getProtoEnum() { 234 return BiometricsProto.CM_INTERNAL_CLEANUP; 235 } 236 237 @VisibleForTesting getCurrentEnumerateClient()238 public InternalEnumerateClient<T> getCurrentEnumerateClient() { 239 return (InternalEnumerateClient<T>) mCurrentTask; 240 } 241 242 @VisibleForTesting getCurrentRemoveClient()243 public RemovalClient<S, T> getCurrentRemoveClient() { 244 return (RemovalClient<S, T>) mCurrentTask; 245 } 246 247 @VisibleForTesting getUnknownHALTemplates()248 public ArrayList<UserTemplate> getUnknownHALTemplates() { 249 return mUnknownHALTemplates; 250 } 251 } 252