• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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