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