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