1 /* 2 * Copyright (C) 2022 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.settings.biometrics2.ui.viewmodel; 18 19 import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL; 20 21 import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_REMAINING; 22 import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS; 23 24 import android.app.Application; 25 import android.content.res.Resources; 26 import android.hardware.fingerprint.FingerprintManager.EnrollReason; 27 import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; 28 import android.os.CancellationSignal; 29 import android.os.SystemClock; 30 import android.util.Log; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 import androidx.lifecycle.AndroidViewModel; 35 import androidx.lifecycle.LiveData; 36 import androidx.lifecycle.MutableLiveData; 37 38 import com.android.settings.R; 39 import com.android.settings.biometrics.fingerprint.FingerprintUpdater; 40 import com.android.settings.biometrics.fingerprint.MessageDisplayController; 41 import com.android.settings.biometrics2.ui.model.EnrollmentProgress; 42 import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage; 43 44 /** 45 * Progress ViewModel handles the state around biometric enrollment. It manages the state of 46 * enrollment throughout the activity lifecycle so the app can continue after an event like 47 * rotation. 48 */ 49 public class FingerprintEnrollProgressViewModel extends AndroidViewModel { 50 51 private static final boolean DEBUG = false; 52 private static final String TAG = "FingerprintEnrollProgressViewModel"; 53 54 private final MutableLiveData<EnrollmentProgress> mProgressLiveData = new MutableLiveData<>( 55 new EnrollmentProgress(INITIAL_STEPS, INITIAL_REMAINING)); 56 private final MutableLiveData<EnrollmentStatusMessage> mHelpMessageLiveData = 57 new MutableLiveData<>(); 58 private final MutableLiveData<EnrollmentStatusMessage> mErrorMessageLiveData = 59 new MutableLiveData<>(); 60 private final MutableLiveData<Boolean> mAcquireLiveData = new MutableLiveData<>(); 61 private final MutableLiveData<Integer> mPointerDownLiveData = new MutableLiveData<>(); 62 private final MutableLiveData<Integer> mPointerUpLiveData = new MutableLiveData<>(); 63 64 private byte[] mToken = null; 65 private final int mUserId; 66 67 private final FingerprintUpdater mFingerprintUpdater; 68 @Nullable private CancellationSignal mCancellationSignal = null; 69 private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() { 70 71 @Override 72 public void onEnrollmentProgress(int remaining) { 73 final int currentSteps = getSteps(); 74 final EnrollmentProgress progress = new EnrollmentProgress( 75 currentSteps == INITIAL_STEPS ? remaining : getSteps(), remaining); 76 if (DEBUG) { 77 Log.d(TAG, "onEnrollmentProgress(" + remaining + "), steps: " + currentSteps 78 + ", post progress as " + progress); 79 } 80 mHelpMessageLiveData.setValue(null); 81 mProgressLiveData.postValue(progress); 82 } 83 84 @Override 85 public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { 86 if (DEBUG) { 87 Log.d(TAG, "onEnrollmentHelp(" + helpMsgId + ", " + helpString + ")"); 88 } 89 mHelpMessageLiveData.postValue(new EnrollmentStatusMessage(helpMsgId, helpString)); 90 } 91 92 @Override 93 public void onEnrollmentError(int errMsgId, CharSequence errString) { 94 if (DEBUG) { 95 Log.d(TAG, "onEnrollmentError(" + errMsgId + ", " + errString + ")"); 96 } 97 mErrorMessageLiveData.postValue(new EnrollmentStatusMessage(errMsgId, errString)); 98 } 99 100 @Override 101 public void onAcquired(boolean isAcquiredGood) { 102 mAcquireLiveData.postValue(isAcquiredGood); 103 } 104 105 @Override 106 public void onUdfpsPointerDown(int sensorId) { 107 mPointerDownLiveData.postValue(sensorId); 108 } 109 110 @Override 111 public void onUdfpsPointerUp(int sensorId) { 112 mPointerUpLiveData.postValue(sensorId); 113 } 114 }; 115 FingerprintEnrollProgressViewModel(@onNull Application application, @NonNull FingerprintUpdater fingerprintUpdater, int userId)116 public FingerprintEnrollProgressViewModel(@NonNull Application application, 117 @NonNull FingerprintUpdater fingerprintUpdater, int userId) { 118 super(application); 119 mFingerprintUpdater = fingerprintUpdater; 120 mUserId = userId; 121 } 122 setToken(byte[] token)123 public void setToken(byte[] token) { 124 mToken = token; 125 } 126 127 /** 128 * clear progress 129 */ clearProgressLiveData()130 public void clearProgressLiveData() { 131 mProgressLiveData.setValue(new EnrollmentProgress(INITIAL_STEPS, INITIAL_REMAINING)); 132 mHelpMessageLiveData.setValue(null); 133 mErrorMessageLiveData.setValue(null); 134 } 135 136 /** 137 * clear error message 138 */ clearErrorMessageLiveData()139 public void clearErrorMessageLiveData() { 140 mErrorMessageLiveData.setValue(null); 141 } 142 getProgressLiveData()143 public LiveData<EnrollmentProgress> getProgressLiveData() { 144 return mProgressLiveData; 145 } 146 getHelpMessageLiveData()147 public LiveData<EnrollmentStatusMessage> getHelpMessageLiveData() { 148 return mHelpMessageLiveData; 149 } 150 getErrorMessageLiveData()151 public LiveData<EnrollmentStatusMessage> getErrorMessageLiveData() { 152 return mErrorMessageLiveData; 153 } 154 getAcquireLiveData()155 public LiveData<Boolean> getAcquireLiveData() { 156 return mAcquireLiveData; 157 } 158 getPointerDownLiveData()159 public LiveData<Integer> getPointerDownLiveData() { 160 return mPointerDownLiveData; 161 } 162 getPointerUpLiveData()163 public LiveData<Integer> getPointerUpLiveData() { 164 return mPointerUpLiveData; 165 } 166 167 /** 168 * Starts enrollment and return latest isEnrolling() result 169 */ startEnrollment(@nrollReason int reason)170 public boolean startEnrollment(@EnrollReason int reason) { 171 if (mToken == null) { 172 Log.e(TAG, "Null hardware auth token for enroll"); 173 return false; 174 } 175 if (mCancellationSignal != null) { 176 Log.w(TAG, "Enrolling has started, shall not start again"); 177 return true; 178 } 179 if (DEBUG) { 180 Log.e(TAG, "startEnrollment(" + reason + ")"); 181 } 182 183 // Clear data 184 mProgressLiveData.setValue(new EnrollmentProgress(INITIAL_STEPS, INITIAL_REMAINING)); 185 mHelpMessageLiveData.setValue(null); 186 mErrorMessageLiveData.setValue(null); 187 188 mCancellationSignal = new CancellationSignal(); 189 190 final Resources res = getApplication().getResources(); 191 if (reason == ENROLL_ENROLL 192 && res.getBoolean(R.bool.enrollment_message_display_controller_flag)) { 193 final EnrollmentCallback callback = new MessageDisplayController( 194 getApplication().getMainThreadHandler(), 195 mEnrollmentCallback, 196 SystemClock.elapsedRealtimeClock(), 197 res.getInteger(R.integer.enrollment_help_minimum_time_display), 198 res.getInteger(R.integer.enrollment_progress_minimum_time_display), 199 res.getBoolean(R.bool.enrollment_progress_priority_over_help), 200 res.getBoolean(R.bool.enrollment_prioritize_acquire_messages), 201 res.getInteger(R.integer.enrollment_collect_time)); 202 mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason); 203 } else { 204 mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback, 205 reason); 206 } 207 return true; 208 } 209 210 /** 211 * Cancels enrollment and return latest isEnrolling result 212 */ cancelEnrollment()213 public boolean cancelEnrollment() { 214 final CancellationSignal cancellationSignal = mCancellationSignal; 215 if (cancellationSignal == null) { 216 Log.e(TAG, "Fail to cancel enrollment, has cancelled or not start"); 217 return false; 218 } 219 220 mCancellationSignal = null; 221 cancellationSignal.cancel(); 222 return true; 223 } 224 isEnrolling()225 public boolean isEnrolling() { 226 return (mCancellationSignal != null); 227 } 228 getSteps()229 private int getSteps() { 230 return mProgressLiveData.getValue().getSteps(); 231 } 232 } 233