Skip to content

Commit 9431366

Browse files
author
Brian Colonna
committed
Separated FUL functionality from LockPatternKeyguardView
This is the first step toward fix 5460649. All of the FUL functions were pulled out of LockPatternKeyguardView into their own FaceUnlock class. LockPatternKeyguardView now has an mFaceUnlock member, which is new'd inside of the LockPatternKeyguardView constructor, passing it some objects it needs to do FUL. FUL calls are now made from LockPatternKeyguardView by doing mFaceUnlock.foo(). Some of the function names were reduced to avoid redundancy (e.g. mFaceUnlock.start() instead of mFaceUnlock.startFaceLock()). This change is just a refactoring and is not intended to change any functionality. There will be other cleanups in the near future...this is basically just the minimum changes needed to get the FUL functionality into its own class. Change-Id: I7dc5b22857bbf1659238b0e2d113069f7bf9ffe7
1 parent 2cde1f9 commit 9431366

File tree

2 files changed

+401
-341
lines changed

2 files changed

+401
-341
lines changed
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
/*
2+
* Copyright (C) 2011 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.internal.policy.impl;
18+
19+
import com.android.internal.R;
20+
import com.android.internal.policy.IFaceLockCallback;
21+
import com.android.internal.policy.IFaceLockInterface;
22+
import com.android.internal.widget.LockPatternUtils;
23+
24+
import android.content.ComponentName;
25+
import android.content.Context;
26+
import android.content.Intent;
27+
import android.content.ServiceConnection;
28+
import android.os.Handler;
29+
import android.os.IBinder;
30+
import android.os.Message;
31+
import android.os.RemoteException;
32+
import android.telephony.TelephonyManager;
33+
import android.util.Log;
34+
import android.view.View;
35+
36+
public class FaceUnlock implements Handler.Callback {
37+
38+
private static final boolean DEBUG = false;
39+
private static final String TAG = "FULLockscreen";
40+
41+
private final Context mContext;
42+
private final KeyguardUpdateMonitor mUpdateMonitor;
43+
44+
private IFaceLockInterface mService;
45+
private boolean mBoundToService = false;
46+
private View mAreaView;
47+
48+
private Handler mHandler;
49+
private final int MSG_SHOW_AREA_VIEW = 0;
50+
private final int MSG_HIDE_AREA_VIEW = 1;
51+
52+
private boolean mServiceRunning = false;
53+
private final Object mServiceRunningLock = new Object();
54+
55+
// Long enough to stay visible while dialer comes up
56+
// Short enough to not be visible if the user goes back immediately
57+
private final int VIEW_AREA_EMERGENCY_DIALER_TIMEOUT = 1000;
58+
59+
// Long enough to stay visible while the service starts
60+
// Short enough to not have to wait long for backup if service fails to start or crashes
61+
// The service can take a couple of seconds to start on the first try after boot
62+
private final int VIEW_AREA_SERVICE_TIMEOUT = 3000;
63+
64+
// So the user has a consistent amount of time when brought to the backup method from FaceLock
65+
private final int BACKUP_LOCK_TIMEOUT = 5000;
66+
67+
/**
68+
* Used to lookup the state of the lock pattern
69+
*/
70+
private final LockPatternUtils mLockPatternUtils;
71+
72+
KeyguardScreenCallback mKeyguardScreenCallback;
73+
74+
public FaceUnlock(Context context, KeyguardUpdateMonitor updateMonitor,
75+
LockPatternUtils lockPatternUtils, KeyguardScreenCallback keyguardScreenCallback) {
76+
mContext = context;
77+
mUpdateMonitor = updateMonitor;
78+
mLockPatternUtils = lockPatternUtils;
79+
mKeyguardScreenCallback = keyguardScreenCallback;
80+
mHandler = new Handler(this);
81+
}
82+
83+
public void cleanUp() {
84+
if (mService != null) {
85+
try {
86+
mService.unregisterCallback(mFaceLockCallback);
87+
} catch (RemoteException e) {
88+
// Not much we can do
89+
}
90+
stop();
91+
mService = null;
92+
}
93+
}
94+
95+
/** When screen is turned on and focused, need to bind to FaceLock service if we are using
96+
* FaceLock, but only if we're not dealing with a call
97+
*/
98+
public void activateIfAble(boolean hasOverlay) {
99+
final boolean tooManyFaceUnlockTries = mUpdateMonitor.getMaxFaceUnlockAttemptsReached();
100+
final int failedBackupAttempts = mUpdateMonitor.getFailedAttempts();
101+
final boolean backupIsTimedOut =
102+
(failedBackupAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
103+
if (tooManyFaceUnlockTries) Log.i(TAG, "tooManyFaceUnlockTries: " + tooManyFaceUnlockTries);
104+
if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
105+
&& installedAndSelected()
106+
&& !hasOverlay
107+
&& !tooManyFaceUnlockTries
108+
&& !backupIsTimedOut) {
109+
bind();
110+
111+
// Show FaceLock area, but only for a little bit so lockpattern will become visible if
112+
// FaceLock fails to start or crashes
113+
showAreaWithTimeout(VIEW_AREA_SERVICE_TIMEOUT);
114+
115+
// When switching between portrait and landscape view while FaceLock is running, the
116+
// screen will eventually go dark unless we poke the wakelock when FaceLock is
117+
// restarted
118+
mKeyguardScreenCallback.pokeWakelock();
119+
} else {
120+
hideArea();
121+
}
122+
}
123+
124+
public boolean isServiceRunning() {
125+
return mServiceRunning;
126+
}
127+
128+
public int viewAreaEmergencyDialerTimeout() {
129+
return VIEW_AREA_EMERGENCY_DIALER_TIMEOUT;
130+
}
131+
132+
// Indicates whether FaceLock is in use
133+
public boolean installedAndSelected() {
134+
return (mLockPatternUtils.usingBiometricWeak() &&
135+
mLockPatternUtils.isBiometricWeakInstalled());
136+
}
137+
138+
// Takes care of FaceLock area when layout is created
139+
public void initializeAreaView(View view) {
140+
if (installedAndSelected()) {
141+
mAreaView = view.findViewById(R.id.faceLockAreaView);
142+
if (mAreaView == null) {
143+
Log.e(TAG, "Layout does not have areaView and FaceLock is enabled");
144+
}
145+
} else {
146+
mAreaView = null; // Set to null if not using FaceLock
147+
}
148+
}
149+
150+
// Stops FaceLock if it is running and reports back whether it was running or not
151+
public boolean stopIfRunning() {
152+
if (installedAndSelected() && mBoundToService) {
153+
stopAndUnbind();
154+
return true;
155+
}
156+
return false;
157+
}
158+
159+
// Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
160+
// This needs to be done in a handler because the call could be coming from a callback from the
161+
// FaceLock service that is in a thread that can't modify the UI
162+
@Override
163+
public boolean handleMessage(Message msg) {
164+
switch (msg.what) {
165+
case MSG_SHOW_AREA_VIEW:
166+
if (mAreaView != null) {
167+
mAreaView.setVisibility(View.VISIBLE);
168+
}
169+
break;
170+
case MSG_HIDE_AREA_VIEW:
171+
if (mAreaView != null) {
172+
mAreaView.setVisibility(View.INVISIBLE);
173+
}
174+
break;
175+
default:
176+
Log.w(TAG, "Unhandled message");
177+
return false;
178+
}
179+
return true;
180+
}
181+
182+
// Removes show and hide messages from the message queue
183+
private void removeAreaDisplayMessages() {
184+
mHandler.removeMessages(MSG_SHOW_AREA_VIEW);
185+
mHandler.removeMessages(MSG_HIDE_AREA_VIEW);
186+
}
187+
188+
// Shows the FaceLock area immediately
189+
public void showArea() {
190+
// Remove messages to prevent a delayed hide message from undo-ing the show
191+
removeAreaDisplayMessages();
192+
mHandler.sendEmptyMessage(MSG_SHOW_AREA_VIEW);
193+
}
194+
195+
// Hides the FaceLock area immediately
196+
public void hideArea() {
197+
// Remove messages to prevent a delayed show message from undo-ing the hide
198+
removeAreaDisplayMessages();
199+
mHandler.sendEmptyMessage(MSG_HIDE_AREA_VIEW);
200+
}
201+
202+
// Shows the FaceLock area for a period of time
203+
public void showAreaWithTimeout(long timeoutMillis) {
204+
showArea();
205+
mHandler.sendEmptyMessageDelayed(MSG_HIDE_AREA_VIEW, timeoutMillis);
206+
}
207+
208+
// Binds to FaceLock service. This call does not tell it to start, but it causes the service
209+
// to call the onServiceConnected callback, which then starts FaceLock.
210+
public void bind() {
211+
if (installedAndSelected()) {
212+
if (!mBoundToService) {
213+
if (DEBUG) Log.d(TAG, "before bind to FaceLock service");
214+
mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
215+
mConnection,
216+
Context.BIND_AUTO_CREATE);
217+
if (DEBUG) Log.d(TAG, "after bind to FaceLock service");
218+
mBoundToService = true;
219+
} else {
220+
Log.w(TAG, "Attempt to bind to FaceLock when already bound");
221+
}
222+
}
223+
}
224+
225+
// Tells FaceLock to stop and then unbinds from the FaceLock service
226+
public void stopAndUnbind() {
227+
if (installedAndSelected()) {
228+
stop();
229+
230+
if (mBoundToService) {
231+
if (DEBUG) Log.d(TAG, "before unbind from FaceLock service");
232+
if (mService != null) {
233+
try {
234+
mService.unregisterCallback(mFaceLockCallback);
235+
} catch (RemoteException e) {
236+
// Not much we can do
237+
}
238+
}
239+
mContext.unbindService(mConnection);
240+
if (DEBUG) Log.d(TAG, "after unbind from FaceLock service");
241+
mBoundToService = false;
242+
} else {
243+
// This is usually not an error when this happens. Sometimes we will tell it to
244+
// unbind multiple times because it's called from both onWindowFocusChanged and
245+
// onDetachedFromWindow.
246+
if (DEBUG) Log.d(TAG, "Attempt to unbind from FaceLock when not bound");
247+
}
248+
}
249+
}
250+
251+
private ServiceConnection mConnection = new ServiceConnection() {
252+
// Completes connection, registers callback and starts FaceLock when service is bound
253+
@Override
254+
public void onServiceConnected(ComponentName className, IBinder iservice) {
255+
mService = IFaceLockInterface.Stub.asInterface(iservice);
256+
if (DEBUG) Log.d(TAG, "Connected to FaceLock service");
257+
try {
258+
mService.registerCallback(mFaceLockCallback);
259+
} catch (RemoteException e) {
260+
Log.e(TAG, "Caught exception connecting to FaceLock: " + e.toString());
261+
mService = null;
262+
mBoundToService = false;
263+
return;
264+
}
265+
266+
if (mAreaView != null) {
267+
int[] position;
268+
position = new int[2];
269+
mAreaView.getLocationInWindow(position);
270+
start(mAreaView.getWindowToken(), position[0], position[1],
271+
mAreaView.getWidth(), mAreaView.getHeight());
272+
}
273+
}
274+
275+
// Cleans up if FaceLock service unexpectedly disconnects
276+
@Override
277+
public void onServiceDisconnected(ComponentName className) {
278+
synchronized(mServiceRunningLock) {
279+
mService = null;
280+
mServiceRunning = false;
281+
}
282+
mBoundToService = false;
283+
Log.w(TAG, "Unexpected disconnect from FaceLock service");
284+
}
285+
};
286+
287+
// Tells the FaceLock service to start displaying its UI and perform recognition
288+
public void start(IBinder windowToken, int x, int y, int w, int h) {
289+
if (installedAndSelected()) {
290+
synchronized (mServiceRunningLock) {
291+
if (!mServiceRunning) {
292+
if (DEBUG) Log.d(TAG, "Starting FaceLock");
293+
try {
294+
mService.startUi(windowToken, x, y, w, h);
295+
} catch (RemoteException e) {
296+
Log.e(TAG, "Caught exception starting FaceLock: " + e.toString());
297+
return;
298+
}
299+
mServiceRunning = true;
300+
} else {
301+
if (DEBUG) Log.w(TAG, "start() attempted while running");
302+
}
303+
}
304+
}
305+
}
306+
307+
// Tells the FaceLock service to stop displaying its UI and stop recognition
308+
public void stop() {
309+
if (installedAndSelected()) {
310+
// Note that attempting to stop FaceLock when it's not running is not an issue.
311+
// FaceLock can return, which stops it and then we try to stop it when the
312+
// screen is turned off. That's why we check.
313+
synchronized (mServiceRunningLock) {
314+
if (mServiceRunning) {
315+
try {
316+
if (DEBUG) Log.d(TAG, "Stopping FaceLock");
317+
mService.stopUi();
318+
} catch (RemoteException e) {
319+
Log.e(TAG, "Caught exception stopping FaceLock: " + e.toString());
320+
}
321+
mServiceRunning = false;
322+
}
323+
}
324+
}
325+
}
326+
327+
// Implements the FaceLock service callback interface defined in AIDL
328+
private final IFaceLockCallback mFaceLockCallback = new IFaceLockCallback.Stub() {
329+
// Stops the FaceLock UI and indicates that the phone should be unlocked
330+
@Override
331+
public void unlock() {
332+
if (DEBUG) Log.d(TAG, "FaceLock unlock()");
333+
showArea(); // Keep fallback covered
334+
stopAndUnbind();
335+
336+
mKeyguardScreenCallback.keyguardDone(true);
337+
mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
338+
}
339+
340+
// Stops the FaceLock UI and exposes the backup method without unlocking
341+
// This means the user has cancelled out
342+
@Override
343+
public void cancel() {
344+
if (DEBUG) Log.d(TAG, "FaceLock cancel()");
345+
hideArea(); // Expose fallback
346+
stopAndUnbind();
347+
mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
348+
}
349+
350+
// Stops the FaceLock UI and exposes the backup method without unlocking
351+
// This means FaceLock failed to recognize them
352+
@Override
353+
public void reportFailedAttempt() {
354+
if (DEBUG) Log.d(TAG, "FaceLock reportFailedAttempt()");
355+
mUpdateMonitor.reportFailedFaceUnlockAttempt();
356+
hideArea(); // Expose fallback
357+
stopAndUnbind();
358+
mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
359+
}
360+
361+
// Removes the black area that covers the backup unlock method
362+
@Override
363+
public void exposeFallback() {
364+
if (DEBUG) Log.d(TAG, "FaceLock exposeFallback()");
365+
hideArea(); // Expose fallback
366+
}
367+
368+
// Allows the Face Unlock service to poke the wake lock to keep the lockscreen alive
369+
@Override
370+
public void pokeWakelock() {
371+
if (DEBUG) Log.d(TAG, "FaceLock pokeWakelock()");
372+
mKeyguardScreenCallback.pokeWakelock();
373+
}
374+
};
375+
}

0 commit comments

Comments
 (0)