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