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 */, userId); 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 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 * {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and 168 * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}. 169 */ showPromptReason(int reason)170 public void showPromptReason(int reason) { 171 mSecurityContainer.showPromptReason(reason); 172 } 173 showMessage(String message, int color)174 public void showMessage(String message, int color) { 175 mSecurityContainer.showMessage(message, color); 176 } 177 178 /** 179 * Dismisses the keyguard by going to the next screen or making it gone. 180 * @param targetUserId a user that needs to be the foreground user at the dismissal completion. 181 * @return True if the keyguard is done. 182 */ dismiss(int targetUserId)183 public boolean dismiss(int targetUserId) { 184 return dismiss(false, targetUserId); 185 } 186 handleBackKey()187 public boolean handleBackKey() { 188 if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { 189 mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser()); 190 return true; 191 } 192 return false; 193 } 194 195 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)196 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 197 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 198 event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription()); 199 return true; 200 } else { 201 return super.dispatchPopulateAccessibilityEvent(event); 202 } 203 } 204 getSecurityContainer()205 protected KeyguardSecurityContainer getSecurityContainer() { 206 return mSecurityContainer; 207 } 208 209 @Override dismiss(boolean authenticated, int targetUserId)210 public boolean dismiss(boolean authenticated, int targetUserId) { 211 return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId); 212 } 213 214 /** 215 * Authentication has happened and it's time to dismiss keyguard. This function 216 * should clean up and inform KeyguardViewMediator. 217 * 218 * @param strongAuth whether the user has authenticated with strong authentication like 219 * pattern, password or PIN but not by trust agents or fingerprint 220 * @param targetUserId a user that needs to be the foreground user at the dismissal completion. 221 */ 222 @Override finish(boolean strongAuth, int targetUserId)223 public void finish(boolean strongAuth, int targetUserId) { 224 // If there's a pending runnable because the user interacted with a widget 225 // and we're leaving keyguard, then run it. 226 boolean deferKeyguardDone = false; 227 if (mDismissAction != null) { 228 deferKeyguardDone = mDismissAction.onDismiss(); 229 mDismissAction = null; 230 mCancelAction = null; 231 } 232 if (mViewMediatorCallback != null) { 233 if (deferKeyguardDone) { 234 mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId); 235 } else { 236 mViewMediatorCallback.keyguardDone(strongAuth, targetUserId); 237 } 238 } 239 } 240 241 @Override reset()242 public void reset() { 243 mViewMediatorCallback.resetKeyguard(); 244 } 245 246 @Override onSecurityModeChanged(SecurityMode securityMode, boolean needsInput)247 public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { 248 if (mViewMediatorCallback != null) { 249 mViewMediatorCallback.setNeedsInput(needsInput); 250 } 251 } 252 userActivity()253 public void userActivity() { 254 if (mViewMediatorCallback != null) { 255 mViewMediatorCallback.userActivity(); 256 } 257 } 258 259 /** 260 * Called when the Keyguard is not actively shown anymore on the screen. 261 */ onPause()262 public void onPause() { 263 if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", 264 Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); 265 mSecurityContainer.showPrimarySecurityScreen(true); 266 mSecurityContainer.onPause(); 267 clearFocus(); 268 } 269 270 /** 271 * Called when the Keyguard is actively shown on the screen. 272 */ onResume()273 public void onResume() { 274 if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); 275 mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); 276 requestFocus(); 277 } 278 279 /** 280 * Starts the animation when the Keyguard gets shown. 281 */ startAppearAnimation()282 public void startAppearAnimation() { 283 mSecurityContainer.startAppearAnimation(); 284 } 285 startDisappearAnimation(Runnable finishRunnable)286 public void startDisappearAnimation(Runnable finishRunnable) { 287 if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) { 288 finishRunnable.run(); 289 } 290 } 291 292 /** 293 * Called before this view is being removed. 294 */ cleanUp()295 public void cleanUp() { 296 getSecurityContainer().onPause(); 297 } 298 299 @Override dispatchKeyEvent(KeyEvent event)300 public boolean dispatchKeyEvent(KeyEvent event) { 301 if (interceptMediaKey(event)) { 302 return true; 303 } 304 return super.dispatchKeyEvent(event); 305 } 306 307 /** 308 * Allows the media keys to work when the keyguard is showing. 309 * The media keys should be of no interest to the actual keyguard view(s), 310 * so intercepting them here should not be of any harm. 311 * @param event The key event 312 * @return whether the event was consumed as a media key. 313 */ interceptMediaKey(KeyEvent event)314 public boolean interceptMediaKey(KeyEvent event) { 315 final int keyCode = event.getKeyCode(); 316 if (event.getAction() == KeyEvent.ACTION_DOWN) { 317 switch (keyCode) { 318 case KeyEvent.KEYCODE_MEDIA_PLAY: 319 case KeyEvent.KEYCODE_MEDIA_PAUSE: 320 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 321 /* Suppress PLAY/PAUSE toggle when phone is ringing or 322 * in-call to avoid music playback */ 323 if (mTelephonyManager == null) { 324 mTelephonyManager = (TelephonyManager) getContext().getSystemService( 325 Context.TELEPHONY_SERVICE); 326 } 327 if (mTelephonyManager != null && 328 mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { 329 return true; // suppress key event 330 } 331 case KeyEvent.KEYCODE_MUTE: 332 case KeyEvent.KEYCODE_HEADSETHOOK: 333 case KeyEvent.KEYCODE_MEDIA_STOP: 334 case KeyEvent.KEYCODE_MEDIA_NEXT: 335 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 336 case KeyEvent.KEYCODE_MEDIA_REWIND: 337 case KeyEvent.KEYCODE_MEDIA_RECORD: 338 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 339 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 340 handleMediaKeyEvent(event); 341 return true; 342 } 343 344 case KeyEvent.KEYCODE_VOLUME_UP: 345 case KeyEvent.KEYCODE_VOLUME_DOWN: 346 case KeyEvent.KEYCODE_VOLUME_MUTE: { 347 if (KEYGUARD_MANAGES_VOLUME) { 348 synchronized (this) { 349 if (mAudioManager == null) { 350 mAudioManager = (AudioManager) getContext().getSystemService( 351 Context.AUDIO_SERVICE); 352 } 353 } 354 // Volume buttons should only function for music (local or remote). 355 // TODO: Actually handle MUTE. 356 mAudioManager.adjustSuggestedStreamVolume( 357 keyCode == KeyEvent.KEYCODE_VOLUME_UP 358 ? AudioManager.ADJUST_RAISE 359 : AudioManager.ADJUST_LOWER /* direction */, 360 AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); 361 // Don't execute default volume behavior 362 return true; 363 } else { 364 return false; 365 } 366 } 367 } 368 } else if (event.getAction() == KeyEvent.ACTION_UP) { 369 switch (keyCode) { 370 case KeyEvent.KEYCODE_MUTE: 371 case KeyEvent.KEYCODE_HEADSETHOOK: 372 case KeyEvent.KEYCODE_MEDIA_PLAY: 373 case KeyEvent.KEYCODE_MEDIA_PAUSE: 374 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 375 case KeyEvent.KEYCODE_MEDIA_STOP: 376 case KeyEvent.KEYCODE_MEDIA_NEXT: 377 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 378 case KeyEvent.KEYCODE_MEDIA_REWIND: 379 case KeyEvent.KEYCODE_MEDIA_RECORD: 380 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 381 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { 382 handleMediaKeyEvent(event); 383 return true; 384 } 385 } 386 } 387 return false; 388 } 389 handleMediaKeyEvent(KeyEvent keyEvent)390 private void handleMediaKeyEvent(KeyEvent keyEvent) { 391 synchronized (this) { 392 if (mAudioManager == null) { 393 mAudioManager = (AudioManager) getContext().getSystemService( 394 Context.AUDIO_SERVICE); 395 } 396 } 397 mAudioManager.dispatchMediaKeyEvent(keyEvent); 398 } 399 400 @Override dispatchSystemUiVisibilityChanged(int visibility)401 public void dispatchSystemUiVisibilityChanged(int visibility) { 402 super.dispatchSystemUiVisibilityChanged(visibility); 403 404 if (!(mContext instanceof Activity)) { 405 setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); 406 } 407 } 408 409 /** 410 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 411 * some cases where we wish to disable it, notably when the menu button placement or technology 412 * is prone to false positives. 413 * 414 * @return true if the menu key should be enabled 415 */ 416 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; shouldEnableMenuKey()417 public boolean shouldEnableMenuKey() { 418 final Resources res = getResources(); 419 final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); 420 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 421 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 422 return !configDisabled || isTestHarness || fileOverride; 423 } 424 setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback)425 public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { 426 mViewMediatorCallback = viewMediatorCallback; 427 // Update ViewMediator with the current input method requirements 428 mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput()); 429 } 430 setLockPatternUtils(LockPatternUtils utils)431 public void setLockPatternUtils(LockPatternUtils utils) { 432 mLockPatternUtils = utils; 433 mSecurityContainer.setLockPatternUtils(utils); 434 } 435 getSecurityMode()436 public SecurityMode getSecurityMode() { 437 return mSecurityContainer.getSecurityMode(); 438 } 439 getCurrentSecurityMode()440 public SecurityMode getCurrentSecurityMode() { 441 return mSecurityContainer.getCurrentSecurityMode(); 442 } 443 } 444