1 /* 2 * Copyright (C) 2007 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.keyguard; 18 19 import android.app.Activity; 20 import android.app.ActivityManager; 21 import android.app.ActivityOptions; 22 import android.app.SearchManager; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.res.Resources; 26 import android.graphics.Canvas; 27 import android.media.AudioManager; 28 import android.media.IAudioService; 29 import android.os.Bundle; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.os.SystemClock; 33 import android.os.UserHandle; 34 import android.telephony.TelephonyManager; 35 import android.util.AttributeSet; 36 import android.util.Log; 37 import android.util.Slog; 38 import android.view.KeyEvent; 39 import android.view.MotionEvent; 40 import android.view.View; 41 import android.view.accessibility.AccessibilityEvent; 42 import android.widget.FrameLayout; 43 44 import com.android.internal.widget.LockPatternUtils; 45 import com.android.keyguard.KeyguardHostView.OnDismissAction; 46 import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; 47 import com.android.keyguard.KeyguardSecurityModel.SecurityMode; 48 49 import java.io.File; 50 51 /** 52 * Base class for keyguard view. {@link #reset} is where you should 53 * reset the state of your view. Use the {@link KeyguardViewCallback} via 54 * {@link #getCallback()} to send information back (such as poking the wake lock, 55 * or finishing the keyguard). 56 * 57 * Handles intercepting of media keys that still work when the keyguard is 58 * showing. 59 */ 60 public abstract class KeyguardViewBase extends FrameLayout implements SecurityCallback { 61 62 private AudioManager mAudioManager; 63 private TelephonyManager mTelephonyManager = null; 64 protected ViewMediatorCallback mViewMediatorCallback; 65 protected LockPatternUtils mLockPatternUtils; 66 private OnDismissAction mDismissAction; 67 68 // Whether the volume keys should be handled by keyguard. If true, then 69 // they will be handled here for specific media types such as music, otherwise 70 // the audio service will bring up the volume dialog. 71 private static final boolean KEYGUARD_MANAGES_VOLUME = false; 72 public static final boolean DEBUG = KeyguardConstants.DEBUG; 73 private static final String TAG = "KeyguardViewBase"; 74 75 private KeyguardSecurityContainer mSecurityContainer; 76 77 public KeyguardViewBase(Context context) { 78 this(context, null); 79 } 80 81 public KeyguardViewBase(Context context, AttributeSet attrs) { 82 super(context, attrs); 83 } 84 85 @Override 86 protected void dispatchDraw(Canvas canvas) { 87 super.dispatchDraw(canvas); 88 if (mViewMediatorCallback != null) { 89 mViewMediatorCallback.keyguardDoneDrawing(); 90 } 91 } 92 93 /** 94 * Sets an action to run when keyguard finishes. 95 * 96 * @param action 97 */ 98 public void setOnDismissAction(OnDismissAction action) { 99 mDismissAction = action; 100 } 101 102 @Override 103 protected void onFinishInflate() { 104 mSecurityContainer = 105 (KeyguardSecurityContainer) findViewById(R.id.keyguard_security_container); 106 mLockPatternUtils = new LockPatternUtils(mContext); 107 mSecurityContainer.setLockPatternUtils(mLockPatternUtils); 108 mSecurityContainer.setSecurityCallback(this); 109 mSecurityContainer.showPrimarySecurityScreen(false); 110 // mSecurityContainer.updateSecurityViews(false /* not bouncing */); 111 } 112 113 /** 114 * Called when the view needs to be shown. 115 */ 116 public void showPrimarySecurityScreen() { 117 if (DEBUG) Log.d(TAG, "show()"); 118 mSecurityContainer.showPrimarySecurityScreen(false); 119 } 120 121 /** 122 * Dismisses the keyguard by going to the next screen or making it gone. 123 * 124 * @return True if the keyguard is done. 125 */ 126 public boolean dismiss() { 127 return dismiss(false); 128 } 129 130 protected void showBouncer(boolean show) { 131 CharSequence what = getContext().getResources().getText( 132 show ? R.string.keyguard_accessibility_show_bouncer 133 : R.string.keyguard_accessibility_hide_bouncer); 134 announceForAccessibility(what); 135 announceCurrentSecurityMethod(); 136 } 137 138 public boolean handleBackKey() { 139 if (mSecurityContainer.getCurrentSecuritySelection() == SecurityMode.Account) { 140 // go back to primary screen 141 mSecurityContainer.showPrimarySecurityScreen(false /*turningOff*/); 142 return true; 143 } 144 if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { 145 mSecurityContainer.dismiss(false); 146 return true; 147 } 148 return false; 149 } 150 151 protected void announceCurrentSecurityMethod() { 152 mSecurityContainer.announceCurrentSecurityMethod(); 153 } 154 155 @Override 156 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 157 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 158 event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription()); 159 return true; 160 } else { 161 return super.dispatchPopulateAccessibilityEvent(event); 162 } 163 } 164 165 protected KeyguardSecurityContainer getSecurityContainer() { 166 return mSecurityContainer; 167 } 168 169 @Override 170 public boolean dismiss(boolean authenticated) { 171 return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated); 172 } 173 174 /** 175 * Authentication has happened and it's time to dismiss keyguard. This function 176 * should clean up and inform KeyguardViewMediator. 177 */ 178 @Override 179 public void finish() { 180 // If the alternate unlock was suppressed, it can now be safely 181 // enabled because the user has left keyguard. 182 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); 183 184 // If there's a pending runnable because the user interacted with a widget 185 // and we're leaving keyguard, then run it. 186 boolean deferKeyguardDone = false; 187 if (mDismissAction != null) { 188 deferKeyguardDone = mDismissAction.onDismiss(); 189 mDismissAction = null; 190 } 191 if (mViewMediatorCallback != null) { 192 if (deferKeyguardDone) { 193 mViewMediatorCallback.keyguardDonePending(); 194 } else { 195 mViewMediatorCallback.keyguardDone(true); 196 } 197 } 198 } 199 200 @Override 201 public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { 202 if (mViewMediatorCallback != null) { 203 mViewMediatorCallback.setNeedsInput(needsInput); 204 } 205 } 206 207 public void userActivity() { 208 if (mViewMediatorCallback != null) { 209 mViewMediatorCallback.userActivity(); 210 } 211 } 212 213 protected void onUserActivityTimeoutChanged() { 214 if (mViewMediatorCallback != null) { 215 mViewMediatorCallback.onUserActivityTimeoutChanged(); 216 } 217 } 218 219 /** 220 * Called when the Keyguard is not actively shown anymore on the screen. 221 */ 222 public void onPause() { 223 if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", 224 Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); 225 // Once the screen turns off, we no longer consider this to be first boot and we want the 226 // biometric unlock to start next time keyguard is shown. 227 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); 228 mSecurityContainer.showPrimarySecurityScreen(true); 229 mSecurityContainer.onPause(); 230 clearFocus(); 231 } 232 233 /** 234 * Called when the Keyguard is actively shown on the screen. 235 */ 236 public void onResume() { 237 if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); 238 mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); 239 requestFocus(); 240 } 241 242 /** 243 * Starts the animation when the Keyguard gets shown. 244 */ 245 public void startAppearAnimation() { 246 mSecurityContainer.startAppearAnimation(); 247 } 248 249 public void startDisappearAnimation(Runnable finishRunnable) { 250 if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) { 251 finishRunnable.run(); 252 } 253 } 254 255 /** 256 * Verify that the user can get past the keyguard securely. This is called, 257 * for example, when the phone disables the keyguard but then wants to launch 258 * something else that requires secure access. 259 * 260 * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)} 261 */ 262 public void verifyUnlock() { 263 SecurityMode securityMode = mSecurityContainer.getSecurityMode(); 264 if (securityMode == KeyguardSecurityModel.SecurityMode.None) { 265 if (mViewMediatorCallback != null) { 266 mViewMediatorCallback.keyguardDone(true); 267 } 268 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern 269 && securityMode != KeyguardSecurityModel.SecurityMode.PIN 270 && securityMode != KeyguardSecurityModel.SecurityMode.Password) { 271 // can only verify unlock when in pattern/password mode 272 if (mViewMediatorCallback != null) { 273 mViewMediatorCallback.keyguardDone(false); 274 } 275 } else { 276 // otherwise, go to the unlock screen, see if they can verify it 277 mSecurityContainer.verifyUnlock(); 278 } 279 } 280 281 /** 282 * Called before this view is being removed. 283 */ 284 abstract public void cleanUp(); 285 286 /** 287 * Gets the desired user activity timeout in milliseconds, or -1 if the 288 * default should be used. 289 */ 290 abstract public long getUserActivityTimeout(); 291 292 @Override 293 public boolean dispatchKeyEvent(KeyEvent event) { 294 if (interceptMediaKey(event)) { 295 return true; 296 } 297 return super.dispatchKeyEvent(event); 298 } 299 300 /** 301 * Allows the media keys to work when the keyguard is showing. 302 * The media keys should be of no interest to the actual keyguard view(s), 303 * so intercepting them here should not be of any harm. 304 * @param event The key event 305 * @return whether the event was consumed as a media key. 306 */ 307 public boolean interceptMediaKey(KeyEvent event) { 308 final int keyCode = event.getKeyCode(); 309 if (event.getAction() == KeyEvent.ACTION_DOWN) { 310 switch (keyCode) { 311 case KeyEvent.KEYCODE_MEDIA_PLAY: 312 case KeyEvent.KEYCODE_MEDIA_PAUSE: 313 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 314 /* Suppress PLAY/PAUSE toggle when phone is ringing or 315 * in-call to avoid music playback */ 316 if (mTelephonyManager == null) { 317 mTelephonyManager = (TelephonyManager) getContext().getSystemService( 318 Context.TELEPHONY_SERVICE); 319 } 320 if (mTelephonyManager != null && 321 mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { 322 return true; // suppress key event 323 } 324 case KeyEvent.KEYCODE_MUTE: 325 case KeyEvent.KEYCODE_HEADSETHOOK: 326 case KeyEvent.KEYCODE_MEDIA_STOP: 327 case KeyEvent.KEYCODE_MEDIA_NEXT: 328 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 329 case KeyEvent.KEYCODE_MEDIA_REWIND: 330 case KeyEvent.KEYCODE_MEDIA_RECORD: 331 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 332 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 333 handleMediaKeyEvent(event); 334 return true; 335 } 336 337 case KeyEvent.KEYCODE_VOLUME_UP: 338 case KeyEvent.KEYCODE_VOLUME_DOWN: 339 case KeyEvent.KEYCODE_VOLUME_MUTE: { 340 if (KEYGUARD_MANAGES_VOLUME) { 341 synchronized (this) { 342 if (mAudioManager == null) { 343 mAudioManager = (AudioManager) getContext().getSystemService( 344 Context.AUDIO_SERVICE); 345 } 346 } 347 // Volume buttons should only function for music (local or remote). 348 // TODO: Actually handle MUTE. 349 mAudioManager.adjustSuggestedStreamVolume( 350 keyCode == KeyEvent.KEYCODE_VOLUME_UP 351 ? AudioManager.ADJUST_RAISE 352 : AudioManager.ADJUST_LOWER /* direction */, 353 AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); 354 // Don't execute default volume behavior 355 return true; 356 } else { 357 return false; 358 } 359 } 360 } 361 } else if (event.getAction() == KeyEvent.ACTION_UP) { 362 switch (keyCode) { 363 case KeyEvent.KEYCODE_MUTE: 364 case KeyEvent.KEYCODE_HEADSETHOOK: 365 case KeyEvent.KEYCODE_MEDIA_PLAY: 366 case KeyEvent.KEYCODE_MEDIA_PAUSE: 367 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 368 case KeyEvent.KEYCODE_MEDIA_STOP: 369 case KeyEvent.KEYCODE_MEDIA_NEXT: 370 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 371 case KeyEvent.KEYCODE_MEDIA_REWIND: 372 case KeyEvent.KEYCODE_MEDIA_RECORD: 373 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 374 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 375 handleMediaKeyEvent(event); 376 return true; 377 } 378 } 379 } 380 return false; 381 } 382 383 private void handleMediaKeyEvent(KeyEvent keyEvent) { 384 synchronized (this) { 385 if (mAudioManager == null) { 386 mAudioManager = (AudioManager) getContext().getSystemService( 387 Context.AUDIO_SERVICE); 388 } 389 } 390 mAudioManager.dispatchMediaKeyEvent(keyEvent); 391 } 392 393 @Override 394 public void dispatchSystemUiVisibilityChanged(int visibility) { 395 super.dispatchSystemUiVisibilityChanged(visibility); 396 397 if (!(mContext instanceof Activity)) { 398 setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); 399 } 400 } 401 402 /** 403 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 404 * some cases where we wish to disable it, notably when the menu button placement or technology 405 * is prone to false positives. 406 * 407 * @return true if the menu key should be enabled 408 */ 409 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 410 private boolean shouldEnableMenuKey() { 411 final Resources res = getResources(); 412 final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); 413 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 414 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 415 return !configDisabled || isTestHarness || fileOverride; 416 } 417 418 public boolean handleMenuKey() { 419 // The following enables the MENU key to work for testing automation 420 if (shouldEnableMenuKey()) { 421 dismiss(); 422 return true; 423 } 424 return false; 425 } 426 427 public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { 428 mViewMediatorCallback = viewMediatorCallback; 429 // Update ViewMediator with the current input method requirements 430 mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput()); 431 } 432 433 protected KeyguardActivityLauncher getActivityLauncher() { 434 return mActivityLauncher; 435 } 436 437 private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() { 438 @Override 439 Context getContext() { 440 return mContext; 441 } 442 443 @Override 444 void setOnDismissAction(OnDismissAction action) { 445 KeyguardViewBase.this.setOnDismissAction(action); 446 } 447 448 @Override 449 LockPatternUtils getLockPatternUtils() { 450 return mLockPatternUtils; 451 } 452 453 @Override 454 void requestDismissKeyguard() { 455 KeyguardViewBase.this.dismiss(false); 456 } 457 }; 458 459 public void showAssistant() { 460 final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 461 .getAssistIntent(mContext, true, UserHandle.USER_CURRENT); 462 463 if (intent == null) return; 464 465 final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 466 R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit, 467 getHandler(), null); 468 469 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 470 mActivityLauncher.launchActivityWithAnimation(intent, false, opts.toBundle(), null, null); 471 } 472 473 public void launchCamera() { 474 mActivityLauncher.launchCamera(getHandler(), null); 475 } 476 477 public void setLockPatternUtils(LockPatternUtils utils) { 478 mLockPatternUtils = utils; 479 mSecurityContainer.setLockPatternUtils(utils); 480 } 481 482 public SecurityMode getSecurityMode() { 483 return mSecurityContainer.getSecurityMode(); 484 } 485 486 public SecurityMode getCurrentSecurityMode() { 487 return mSecurityContainer.getCurrentSecurityMode(); 488 } 489 490 protected abstract void onUserSwitching(boolean switching); 491 492 protected abstract void onCreateOptions(Bundle options); 493 494 protected abstract void onExternalMotionEvent(MotionEvent event); 495 496 } 497