• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.systemui.biometrics;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.graphics.PointF;
23 import android.hardware.biometrics.BiometricOverlayConstants;
24 import android.hardware.fingerprint.FingerprintManager;
25 import android.os.Build;
26 import android.os.UserHandle;
27 import android.util.Log;
28 import android.util.TypedValue;
29 import android.view.accessibility.AccessibilityManager;
30 
31 import com.android.systemui.util.settings.SecureSettings;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * Helps keep track of enrollment state and animates the progress bar accordingly.
38  */
39 public class UdfpsEnrollHelper {
40     private static final String TAG = "UdfpsEnrollHelper";
41 
42     private static final String SCALE_OVERRIDE =
43             "com.android.systemui.biometrics.UdfpsEnrollHelper.scale";
44     private static final float SCALE = 0.5f;
45 
46     private static final String NEW_COORDS_OVERRIDE =
47             "com.android.systemui.biometrics.UdfpsNewCoords";
48 
49     interface Listener {
onEnrollmentProgress(int remaining, int totalSteps)50         void onEnrollmentProgress(int remaining, int totalSteps);
onEnrollmentHelp(int remaining, int totalSteps)51         void onEnrollmentHelp(int remaining, int totalSteps);
onLastStepAcquired()52         void onLastStepAcquired();
53     }
54 
55     @NonNull private final FingerprintManager mFingerprintManager;
56     @NonNull private final SecureSettings mSecureSettings;
57     // IUdfpsOverlayController reason
58     private final int mEnrollReason;
59     private final boolean mAccessibilityEnabled;
60     @NonNull private final List<PointF> mGuidedEnrollmentPoints;
61 
62     private int mTotalSteps = -1;
63     private int mRemainingSteps = -1;
64 
65     // Note that this is actually not equal to "mTotalSteps - mRemainingSteps", because the
66     // interface makes no promises about monotonically increasing by one each time.
67     private int mLocationsEnrolled = 0;
68 
69     private int mCenterTouchCount = 0;
70 
71     @Nullable Listener mListener;
72 
UdfpsEnrollHelper(@onNull Context context, @NonNull FingerprintManager fingerprintManager, SecureSettings secureSettings, int reason)73     public UdfpsEnrollHelper(@NonNull Context context,
74             @NonNull FingerprintManager fingerprintManager, SecureSettings secureSettings,
75             int reason) {
76 
77         mFingerprintManager = fingerprintManager;
78         mSecureSettings = secureSettings;
79         mEnrollReason = reason;
80 
81         final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
82         mAccessibilityEnabled = am.isEnabled();
83 
84         mGuidedEnrollmentPoints = new ArrayList<>();
85 
86         // Number of pixels per mm
87         float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1,
88                 context.getResources().getDisplayMetrics());
89         boolean useNewCoords = mSecureSettings.getIntForUser(NEW_COORDS_OVERRIDE, 0,
90                 UserHandle.USER_CURRENT) != 0;
91         if (useNewCoords && (Build.IS_ENG || Build.IS_USERDEBUG)) {
92             Log.v(TAG, "Using new coordinates");
93             mGuidedEnrollmentPoints.add(new PointF(-0.15f * px, -1.02f * px));
94             mGuidedEnrollmentPoints.add(new PointF(-0.15f * px,  1.02f * px));
95             mGuidedEnrollmentPoints.add(new PointF( 0.29f * px,  0.00f * px));
96             mGuidedEnrollmentPoints.add(new PointF( 2.17f * px, -2.35f * px));
97             mGuidedEnrollmentPoints.add(new PointF( 1.07f * px, -3.96f * px));
98             mGuidedEnrollmentPoints.add(new PointF(-0.37f * px, -4.31f * px));
99             mGuidedEnrollmentPoints.add(new PointF(-1.69f * px, -3.29f * px));
100             mGuidedEnrollmentPoints.add(new PointF(-2.48f * px, -1.23f * px));
101             mGuidedEnrollmentPoints.add(new PointF(-2.48f * px,  1.23f * px));
102             mGuidedEnrollmentPoints.add(new PointF(-1.69f * px,  3.29f * px));
103             mGuidedEnrollmentPoints.add(new PointF(-0.37f * px,  4.31f * px));
104             mGuidedEnrollmentPoints.add(new PointF( 1.07f * px,  3.96f * px));
105             mGuidedEnrollmentPoints.add(new PointF( 2.17f * px,  2.35f * px));
106             mGuidedEnrollmentPoints.add(new PointF( 2.58f * px,  0.00f * px));
107         } else {
108             Log.v(TAG, "Using old coordinates");
109             mGuidedEnrollmentPoints.add(new PointF( 2.00f * px,  0.00f * px));
110             mGuidedEnrollmentPoints.add(new PointF( 0.87f * px, -2.70f * px));
111             mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, -1.31f * px));
112             mGuidedEnrollmentPoints.add(new PointF(-1.80f * px,  1.31f * px));
113             mGuidedEnrollmentPoints.add(new PointF( 0.88f * px,  2.70f * px));
114             mGuidedEnrollmentPoints.add(new PointF( 3.94f * px, -1.06f * px));
115             mGuidedEnrollmentPoints.add(new PointF( 2.90f * px, -4.14f * px));
116             mGuidedEnrollmentPoints.add(new PointF(-0.52f * px, -5.95f * px));
117             mGuidedEnrollmentPoints.add(new PointF(-3.33f * px, -3.33f * px));
118             mGuidedEnrollmentPoints.add(new PointF(-3.99f * px, -0.35f * px));
119             mGuidedEnrollmentPoints.add(new PointF(-3.62f * px,  2.54f * px));
120             mGuidedEnrollmentPoints.add(new PointF(-1.49f * px,  5.57f * px));
121             mGuidedEnrollmentPoints.add(new PointF( 2.29f * px,  4.92f * px));
122             mGuidedEnrollmentPoints.add(new PointF( 3.82f * px,  1.78f * px));
123         }
124     }
125 
getStageCount()126     int getStageCount() {
127         return mFingerprintManager.getEnrollStageCount();
128     }
129 
getStageThresholdSteps(int totalSteps, int stageIndex)130     int getStageThresholdSteps(int totalSteps, int stageIndex) {
131         return Math.round(totalSteps * mFingerprintManager.getEnrollStageThreshold(stageIndex));
132     }
133 
shouldShowProgressBar()134     boolean shouldShowProgressBar() {
135         return mEnrollReason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
136     }
137 
onEnrollmentProgress(int remaining)138     void onEnrollmentProgress(int remaining) {
139         if (mTotalSteps == -1) {
140             mTotalSteps = remaining;
141         }
142 
143         if (remaining != mRemainingSteps) {
144             mLocationsEnrolled++;
145             if (isCenterEnrollmentStage()) {
146                 mCenterTouchCount++;
147             }
148         }
149 
150         mRemainingSteps = remaining;
151 
152         if (mListener != null) {
153             mListener.onEnrollmentProgress(remaining, mTotalSteps);
154         }
155     }
156 
onEnrollmentHelp()157     void onEnrollmentHelp() {
158         if (mListener != null) {
159             mListener.onEnrollmentHelp(mRemainingSteps, mTotalSteps);
160         }
161     }
162 
setListener(Listener listener)163     void setListener(Listener listener) {
164         mListener = listener;
165 
166         // Only notify during setListener if enrollment is already in progress, so the progress
167         // bar can be updated. If enrollment has not started yet, the progress bar will be empty
168         // anyway.
169         if (mListener != null && mTotalSteps != -1) {
170             mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
171         }
172     }
173 
isCenterEnrollmentStage()174     boolean isCenterEnrollmentStage() {
175         if (mTotalSteps == -1 || mRemainingSteps == -1) {
176             return true;
177         }
178         return mTotalSteps - mRemainingSteps < getStageThresholdSteps(mTotalSteps, 0);
179     }
180 
isGuidedEnrollmentStage()181     boolean isGuidedEnrollmentStage() {
182         if (mAccessibilityEnabled || mTotalSteps == -1 || mRemainingSteps == -1) {
183             return false;
184         }
185         final int progressSteps = mTotalSteps - mRemainingSteps;
186         return progressSteps >= getStageThresholdSteps(mTotalSteps, 0)
187                 && progressSteps < getStageThresholdSteps(mTotalSteps, 1);
188     }
189 
isTipEnrollmentStage()190     boolean isTipEnrollmentStage() {
191         if (mTotalSteps == -1 || mRemainingSteps == -1) {
192             return false;
193         }
194         final int progressSteps = mTotalSteps - mRemainingSteps;
195         return progressSteps >= getStageThresholdSteps(mTotalSteps, 1)
196                 && progressSteps < getStageThresholdSteps(mTotalSteps, 2);
197     }
198 
isEdgeEnrollmentStage()199     boolean isEdgeEnrollmentStage() {
200         if (mTotalSteps == -1 || mRemainingSteps == -1) {
201             return false;
202         }
203         return mTotalSteps - mRemainingSteps >= getStageThresholdSteps(mTotalSteps, 2);
204     }
205 
206     @NonNull
getNextGuidedEnrollmentPoint()207     PointF getNextGuidedEnrollmentPoint() {
208         if (mAccessibilityEnabled || !isGuidedEnrollmentStage()) {
209             return new PointF(0f, 0f);
210         }
211 
212         float scale = SCALE;
213         if (Build.IS_ENG || Build.IS_USERDEBUG) {
214             scale = mSecureSettings.getFloatForUser(SCALE_OVERRIDE, SCALE,
215                     UserHandle.USER_CURRENT);
216         }
217         final int index = mLocationsEnrolled - mCenterTouchCount;
218         final PointF originalPoint = mGuidedEnrollmentPoints
219                 .get(index % mGuidedEnrollmentPoints.size());
220         return new PointF(originalPoint.x * scale, originalPoint.y * scale);
221     }
222 
animateIfLastStep()223     void animateIfLastStep() {
224         if (mListener == null) {
225             Log.e(TAG, "animateIfLastStep, null listener");
226             return;
227         }
228 
229         if (mRemainingSteps <= 2 && mRemainingSteps >= 0) {
230             mListener.onLastStepAcquired();
231         }
232     }
233 }
234