2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.keyguard;
19 import android.app.Activity;
20 import android.app.ActivityManager;
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.graphics.Canvas;
24 import android.media.AudioManager;
25 import android.os.Bundle;
26 import android.os.SystemClock;
27 import android.telephony.TelephonyManager;
28 import android.util.AttributeSet;
29 import android.util.Log;
30 import android.view.KeyEvent;
31 import android.view.MotionEvent;
32 import android.widget.FrameLayout;
34 import com.android.internal.widget.LockPatternUtils;
35 import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
36 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
41 * Base class for keyguard view. {@link #reset} is where you should
42 * reset the state of your view. Use the {@link KeyguardViewCallback} via
43 * {@link #getCallback()} to send information back (such as poking the wake lock,
44 * or finishing the keyguard).
46 * Handles intercepting of media keys that still work when the keyguard is
49 public class KeyguardHostView extends FrameLayout implements SecurityCallback {
51 public interface OnDismissAction {
53 * @return true if the dismiss should be deferred
58 private AudioManager mAudioManager;
59 private TelephonyManager mTelephonyManager = null;
60 protected ViewMediatorCallback mViewMediatorCallback;
61 protected LockPatternUtils mLockPatternUtils;
62 private OnDismissAction mDismissAction;
64 private final KeyguardUpdateMonitorCallback mUpdateCallback =
65 new KeyguardUpdateMonitorCallback() {
68 public void onUserSwitchComplete(int userId) {
69 getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);
73 public void onTrustInitiatedByUser(int userId) {
74 if (userId != mLockPatternUtils.getCurrentUser()) return;
75 if (!isAttachedToWindow()) return;
77 if (isVisibleToUser()) {
78 dismiss(false /* authenticated */);
80 mViewMediatorCallback.playTrustedSound();
85 // Whether the volume keys should be handled by keyguard. If true, then
86 // they will be handled here for specific media types such as music, otherwise
87 // the audio service will bring up the volume dialog.
88 private static final boolean KEYGUARD_MANAGES_VOLUME = false;
89 public static final boolean DEBUG = KeyguardConstants.DEBUG;
90 private static final String TAG = "KeyguardViewBase";
92 private KeyguardSecurityContainer mSecurityContainer;
94 public KeyguardHostView(Context context) {
98 public KeyguardHostView(Context context, AttributeSet attrs) {
99 super(context, attrs);
100 KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback);
104 protected void dispatchDraw(Canvas canvas) {
105 super.dispatchDraw(canvas);
106 if (mViewMediatorCallback != null) {
107 mViewMediatorCallback.keyguardDoneDrawing();
112 * Sets an action to run when keyguard finishes.
116 public void setOnDismissAction(OnDismissAction action) {
117 mDismissAction = action;
121 protected void onFinishInflate() {
123 (KeyguardSecurityContainer) findViewById(R.id.keyguard_security_container);
124 mLockPatternUtils = new LockPatternUtils(mContext);
125 mSecurityContainer.setLockPatternUtils(mLockPatternUtils);
126 mSecurityContainer.setSecurityCallback(this);
127 mSecurityContainer.showPrimarySecurityScreen(false);
128 // mSecurityContainer.updateSecurityViews(false /* not bouncing */);
132 * Called when the view needs to be shown.
134 public void showPrimarySecurityScreen() {
135 if (DEBUG) Log.d(TAG, "show()");
136 mSecurityContainer.showPrimarySecurityScreen(false);
140 * Dismisses the keyguard by going to the next screen or making it gone.
142 * @return True if the keyguard is done.
144 public boolean dismiss() {
145 return dismiss(false);
148 public boolean handleBackKey() {
149 if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) {
150 mSecurityContainer.dismiss(false);
156 protected KeyguardSecurityContainer getSecurityContainer() {
157 return mSecurityContainer;
161 public boolean dismiss(boolean authenticated) {
162 return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated);
166 * Authentication has happened and it's time to dismiss keyguard. This function
167 * should clean up and inform KeyguardViewMediator.
170 public void finish() {
171 // If the alternate unlock was suppressed, it can now be safely
172 // enabled because the user has left keyguard.
173 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
175 // If there's a pending runnable because the user interacted with a widget
176 // and we're leaving keyguard, then run it.
177 boolean deferKeyguardDone = false;
178 if (mDismissAction != null) {
179 deferKeyguardDone = mDismissAction.onDismiss();
180 mDismissAction = null;
182 if (mViewMediatorCallback != null) {
183 if (deferKeyguardDone) {
184 mViewMediatorCallback.keyguardDonePending();
186 mViewMediatorCallback.keyguardDone(true);
192 public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
193 if (mViewMediatorCallback != null) {
194 mViewMediatorCallback.setNeedsInput(needsInput);
198 public void userActivity() {
199 if (mViewMediatorCallback != null) {
200 mViewMediatorCallback.userActivity();
205 * Called when the Keyguard is not actively shown anymore on the screen.
207 public void onPause() {
208 if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
209 Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
210 // Once the screen turns off, we no longer consider this to be first boot and we want the
211 // biometric unlock to start next time keyguard is shown.
212 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
213 mSecurityContainer.showPrimarySecurityScreen(true);
214 mSecurityContainer.onPause();
219 * Called when the Keyguard is actively shown on the screen.
221 public void onResume() {
222 if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
223 mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
228 * Starts the animation when the Keyguard gets shown.
230 public void startAppearAnimation() {
231 mSecurityContainer.startAppearAnimation();
234 public void startDisappearAnimation(Runnable finishRunnable) {
235 if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) {
236 finishRunnable.run();
241 * Verify that the user can get past the keyguard securely. This is called,
242 * for example, when the phone disables the keyguard but then wants to launch
243 * something else that requires secure access.
245 * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
247 public void verifyUnlock() {
248 SecurityMode securityMode = mSecurityContainer.getSecurityMode();
249 if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
250 if (mViewMediatorCallback != null) {
251 mViewMediatorCallback.keyguardDone(true);
253 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
254 && securityMode != KeyguardSecurityModel.SecurityMode.PIN
255 && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
256 // can only verify unlock when in pattern/password mode
257 if (mViewMediatorCallback != null) {
258 mViewMediatorCallback.keyguardDone(false);
261 // otherwise, go to the unlock screen, see if they can verify it
262 mSecurityContainer.verifyUnlock();
267 * Called before this view is being removed.
269 public void cleanUp() {
270 getSecurityContainer().onPause();
274 public boolean dispatchKeyEvent(KeyEvent event) {
275 if (interceptMediaKey(event)) {
278 return super.dispatchKeyEvent(event);
282 * Allows the media keys to work when the keyguard is showing.
283 * The media keys should be of no interest to the actual keyguard view(s),
284 * so intercepting them here should not be of any harm.
285 * @param event The key event
286 * @return whether the event was consumed as a media key.
288 public boolean interceptMediaKey(KeyEvent event) {
289 final int keyCode = event.getKeyCode();
290 if (event.getAction() == KeyEvent.ACTION_DOWN) {
292 case KeyEvent.KEYCODE_MEDIA_PLAY:
293 case KeyEvent.KEYCODE_MEDIA_PAUSE:
294 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
295 /* Suppress PLAY/PAUSE toggle when phone is ringing or
296 * in-call to avoid music playback */
297 if (mTelephonyManager == null) {
298 mTelephonyManager = (TelephonyManager) getContext().getSystemService(
299 Context.TELEPHONY_SERVICE);
301 if (mTelephonyManager != null &&
302 mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
303 return true; // suppress key event
305 case KeyEvent.KEYCODE_MUTE:
306 case KeyEvent.KEYCODE_HEADSETHOOK:
307 case KeyEvent.KEYCODE_MEDIA_STOP:
308 case KeyEvent.KEYCODE_MEDIA_NEXT:
309 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
310 case KeyEvent.KEYCODE_MEDIA_REWIND:
311 case KeyEvent.KEYCODE_MEDIA_RECORD:
312 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
313 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
314 handleMediaKeyEvent(event);
318 case KeyEvent.KEYCODE_VOLUME_UP:
319 case KeyEvent.KEYCODE_VOLUME_DOWN:
320 case KeyEvent.KEYCODE_VOLUME_MUTE: {
321 if (KEYGUARD_MANAGES_VOLUME) {
322 synchronized (this) {
323 if (mAudioManager == null) {
324 mAudioManager = (AudioManager) getContext().getSystemService(
325 Context.AUDIO_SERVICE);
328 // Volume buttons should only function for music (local or remote).
329 // TODO: Actually handle MUTE.
330 mAudioManager.adjustSuggestedStreamVolume(
331 keyCode == KeyEvent.KEYCODE_VOLUME_UP
332 ? AudioManager.ADJUST_RAISE
333 : AudioManager.ADJUST_LOWER /* direction */,
334 AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
335 // Don't execute default volume behavior
342 } else if (event.getAction() == KeyEvent.ACTION_UP) {
344 case KeyEvent.KEYCODE_MUTE:
345 case KeyEvent.KEYCODE_HEADSETHOOK:
346 case KeyEvent.KEYCODE_MEDIA_PLAY:
347 case KeyEvent.KEYCODE_MEDIA_PAUSE:
348 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
349 case KeyEvent.KEYCODE_MEDIA_STOP:
350 case KeyEvent.KEYCODE_MEDIA_NEXT:
351 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
352 case KeyEvent.KEYCODE_MEDIA_REWIND:
353 case KeyEvent.KEYCODE_MEDIA_RECORD:
354 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
355 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
356 handleMediaKeyEvent(event);
364 private void handleMediaKeyEvent(KeyEvent keyEvent) {
365 synchronized (this) {
366 if (mAudioManager == null) {
367 mAudioManager = (AudioManager) getContext().getSystemService(
368 Context.AUDIO_SERVICE);
371 mAudioManager.dispatchMediaKeyEvent(keyEvent);
375 public void dispatchSystemUiVisibilityChanged(int visibility) {
376 super.dispatchSystemUiVisibilityChanged(visibility);
378 if (!(mContext instanceof Activity)) {
379 setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
384 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
385 * some cases where we wish to disable it, notably when the menu button placement or technology
386 * is prone to false positives.
388 * @return true if the menu key should be enabled
390 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
391 private boolean shouldEnableMenuKey() {
392 final Resources res = getResources();
393 final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
394 final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
395 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
396 return !configDisabled || isTestHarness || fileOverride;
399 public boolean handleMenuKey() {
400 // The following enables the MENU key to work for testing automation
401 if (shouldEnableMenuKey()) {
408 public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
409 mViewMediatorCallback = viewMediatorCallback;
410 // Update ViewMediator with the current input method requirements
411 mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput());
414 public void setLockPatternUtils(LockPatternUtils utils) {
415 mLockPatternUtils = utils;
416 mSecurityContainer.setLockPatternUtils(utils);
419 public SecurityMode getSecurityMode() {
420 return mSecurityContainer.getSecurityMode();
423 public SecurityMode getCurrentSecurityMode() {
424 return mSecurityContainer.getCurrentSecurityMode();