1 /*
2  * Copyright (C) 2015 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.fingerprint;
18 
19 import android.annotation.Nullable;
20 import android.app.Activity;
21 import android.content.Intent;
22 import android.hardware.fingerprint.FingerprintManager;
23 import android.os.Bundle;
24 import android.os.CancellationSignal;
25 import android.os.Handler;
26 import android.os.UserHandle;
27 
28 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
29 import com.android.settings.ChooseLockSettingsHelper;
30 import com.android.settings.Utils;
31 import com.android.settings.core.InstrumentedFragment;
32 import com.android.settings.password.IFingerprintManager;
33 
34 import java.util.ArrayList;
35 
36 /**
37  * Sidecar fragment to handle the state around fingerprint enrollment.
38  */
39 public class FingerprintEnrollSidecar extends InstrumentedFragment {
40 
41     private int mEnrollmentSteps = -1;
42     private int mEnrollmentRemaining = 0;
43     private Listener mListener;
44     private boolean mEnrolling;
45     private CancellationSignal mEnrollmentCancel;
46     private Handler mHandler = new Handler();
47     private byte[] mToken;
48     private boolean mDone;
49     private int mUserId;
50     private IFingerprintManager mFingerprintManager;
51     private ArrayList<QueuedEvent> mQueuedEvents;
52 
53     private abstract class QueuedEvent {
send(Listener listener)54         public abstract void send(Listener listener);
55     }
56 
57     private class QueuedEnrollmentProgress extends QueuedEvent {
58         int enrollmentSteps;
59         int remaining;
QueuedEnrollmentProgress(int enrollmentSteps, int remaining)60         public QueuedEnrollmentProgress(int enrollmentSteps, int remaining) {
61             this.enrollmentSteps = enrollmentSteps;
62             this.remaining = remaining;
63         }
64 
65         @Override
send(Listener listener)66         public void send(Listener listener) {
67             listener.onEnrollmentProgressChange(enrollmentSteps, remaining);
68         }
69     }
70 
71     private class QueuedEnrollmentHelp extends QueuedEvent {
72         int helpMsgId;
73         CharSequence helpString;
QueuedEnrollmentHelp(int helpMsgId, CharSequence helpString)74         public QueuedEnrollmentHelp(int helpMsgId, CharSequence helpString) {
75             this.helpMsgId = helpMsgId;
76             this.helpString = helpString;
77         }
78 
79         @Override
send(Listener listener)80         public void send(Listener listener) {
81             listener.onEnrollmentHelp(helpString);
82         }
83     }
84 
85     private class QueuedEnrollmentError extends QueuedEvent {
86         int errMsgId;
87         CharSequence errString;
QueuedEnrollmentError(int errMsgId, CharSequence errString)88         public QueuedEnrollmentError(int errMsgId, CharSequence errString) {
89             this.errMsgId = errMsgId;
90             this.errString = errString;
91         }
92 
93         @Override
send(Listener listener)94         public void send(Listener listener) {
95             listener.onEnrollmentError(errMsgId, errString);
96         }
97     }
98 
FingerprintEnrollSidecar()99     public FingerprintEnrollSidecar() {
100         mQueuedEvents = new ArrayList<>();
101     }
102 
103     @Override
onCreate(@ullable Bundle savedInstanceState)104     public void onCreate(@Nullable Bundle savedInstanceState) {
105         super.onCreate(savedInstanceState);
106         setRetainInstance(true);
107     }
108 
109     @Override
onAttach(Activity activity)110     public void onAttach(Activity activity) {
111         super.onAttach(activity);
112         mFingerprintManager = Utils.getFingerprintManagerWrapperOrNull(activity);
113         mToken = activity.getIntent().getByteArrayExtra(
114                 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
115         mUserId = activity.getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
116     }
117 
118     @Override
onStart()119     public void onStart() {
120         super.onStart();
121         if (!mEnrolling) {
122             startEnrollment();
123         }
124     }
125 
126     @Override
onStop()127     public void onStop() {
128         super.onStop();
129         if (!getActivity().isChangingConfigurations()) {
130             cancelEnrollment();
131         }
132     }
133 
startEnrollment()134     private void startEnrollment() {
135         mHandler.removeCallbacks(mTimeoutRunnable);
136         mEnrollmentSteps = -1;
137         mEnrollmentCancel = new CancellationSignal();
138         if (mUserId != UserHandle.USER_NULL) {
139             mFingerprintManager.setActiveUser(mUserId);
140         }
141         mFingerprintManager.enroll(mToken, mEnrollmentCancel,
142                 0 /* flags */, mUserId, mEnrollmentCallback);
143         mEnrolling = true;
144     }
145 
cancelEnrollment()146     boolean cancelEnrollment() {
147         mHandler.removeCallbacks(mTimeoutRunnable);
148         if (mEnrolling) {
149             mEnrollmentCancel.cancel();
150             mEnrolling = false;
151             mEnrollmentSteps = -1;
152             return true;
153         }
154         return false;
155     }
156 
setListener(Listener listener)157     public void setListener(Listener listener) {
158         mListener = listener;
159         if (mListener != null) {
160             for (int i=0; i<mQueuedEvents.size(); i++) {
161                 QueuedEvent event = mQueuedEvents.get(i);
162                 event.send(mListener);
163             }
164             mQueuedEvents.clear();
165         }
166     }
167 
getEnrollmentSteps()168     public int getEnrollmentSteps() {
169         return mEnrollmentSteps;
170     }
171 
getEnrollmentRemaining()172     public int getEnrollmentRemaining() {
173         return mEnrollmentRemaining;
174     }
175 
isDone()176     public boolean isDone() {
177         return mDone;
178     }
179 
180     private FingerprintManager.EnrollmentCallback mEnrollmentCallback
181             = new FingerprintManager.EnrollmentCallback() {
182 
183         @Override
184         public void onEnrollmentProgress(int remaining) {
185             if (mEnrollmentSteps == -1) {
186                 mEnrollmentSteps = remaining;
187             }
188             mEnrollmentRemaining = remaining;
189             mDone = remaining == 0;
190             if (mListener != null) {
191                 mListener.onEnrollmentProgressChange(mEnrollmentSteps, remaining);
192             } else {
193                 mQueuedEvents.add(new QueuedEnrollmentProgress(mEnrollmentSteps, remaining));
194             }
195         }
196 
197         @Override
198         public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
199             if (mListener != null) {
200                 mListener.onEnrollmentHelp(helpString);
201             } else {
202                 mQueuedEvents.add(new QueuedEnrollmentHelp(helpMsgId, helpString));
203             }
204         }
205 
206         @Override
207         public void onEnrollmentError(int errMsgId, CharSequence errString) {
208             if (mListener != null) {
209                 mListener.onEnrollmentError(errMsgId, errString);
210             } else {
211                 mQueuedEvents.add(new QueuedEnrollmentError(errMsgId, errString));
212             }
213             mEnrolling = false;
214         }
215     };
216 
217     private final Runnable mTimeoutRunnable = new Runnable() {
218         @Override
219         public void run() {
220             cancelEnrollment();
221         }
222     };
223 
224     @Override
getMetricsCategory()225     public int getMetricsCategory() {
226         return MetricsEvent.FINGERPRINT_ENROLL_SIDECAR;
227     }
228 
229     public interface Listener {
onEnrollmentHelp(CharSequence helpString)230         void onEnrollmentHelp(CharSequence helpString);
onEnrollmentError(int errMsgId, CharSequence errString)231         void onEnrollmentError(int errMsgId, CharSequence errString);
onEnrollmentProgressChange(int steps, int remaining)232         void onEnrollmentProgressChange(int steps, int remaining);
233     }
234 
isEnrolling()235     public boolean isEnrolling() {
236         return mEnrolling;
237     }
238 }
239