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