Skip to content

Commit 82920ed

Browse files
Brian ColonnaAndroid (Google) Code Review
authored andcommitted
Merge "Separated FUL functionality from LockPatternKeyguardView"
2 parents d9b2070 + 9431366 commit 82920ed

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)