/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.keyguard;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.media.AudioManager;
import android.os.SystemClock;
import android.service.trust.TrustAgentService;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;

import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.Utils;

import java.io.File;

Base class for keyguard view. reset is where you should reset the state of your view. Use the KeyguardViewCallback via getCallback() to send information back (such as poking the wake lock, or finishing the keyguard). Handles intercepting of media keys that still work when the keyguard is showing.
/** * Base class for keyguard view. {@link #reset} is where you should * reset the state of your view. Use the {@link KeyguardViewCallback} via * {@link #getCallback()} to send information back (such as poking the wake lock, * or finishing the keyguard). * * Handles intercepting of media keys that still work when the keyguard is * showing. */
public class KeyguardHostView extends FrameLayout implements SecurityCallback { public interface OnDismissAction {
Returns:true if the dismiss should be deferred
/** * @return true if the dismiss should be deferred */
boolean onDismiss(); } private AudioManager mAudioManager; private TelephonyManager mTelephonyManager = null; protected ViewMediatorCallback mViewMediatorCallback; protected LockPatternUtils mLockPatternUtils; private OnDismissAction mDismissAction; private Runnable mCancelAction; private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override public void onUserSwitchComplete(int userId) { getSecurityContainer().showPrimarySecurityScreen(false /* turning off */); } @Override public void onTrustGrantedWithFlags(int flags, int userId) { if (userId != KeyguardUpdateMonitor.getCurrentUser()) return; if (!isAttachedToWindow()) return; boolean bouncerVisible = isVisibleToUser(); boolean initiatedByUser = (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0; boolean dismissKeyguard = (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0; if (initiatedByUser || dismissKeyguard) { if (mViewMediatorCallback.isScreenOn() && (bouncerVisible || dismissKeyguard)) { if (!bouncerVisible) { // The trust agent dismissed the keyguard without the user proving // that they are present (by swiping up to show the bouncer). That's fine if // the user proved presence via some other way to the trust agent. Log.i(TAG, "TrustAgent dismissed Keyguard."); } dismiss(false /* authenticated */, userId); } else { mViewMediatorCallback.playTrustedSound(); } } } }; // Whether the volume keys should be handled by keyguard. If true, then // they will be handled here for specific media types such as music, otherwise // the audio service will bring up the volume dialog. private static final boolean KEYGUARD_MANAGES_VOLUME = false; public static final boolean DEBUG = KeyguardConstants.DEBUG; private static final String TAG = "KeyguardViewBase"; private KeyguardSecurityContainer mSecurityContainer; public KeyguardHostView(Context context) { this(context, null); } public KeyguardHostView(Context context, AttributeSet attrs) { super(context, attrs); KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mViewMediatorCallback != null) { mViewMediatorCallback.keyguardDoneDrawing(); } }
Sets an action to run when keyguard finishes.
Params:
  • action –
/** * Sets an action to run when keyguard finishes. * * @param action */
public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) { if (mCancelAction != null) { mCancelAction.run(); mCancelAction = null; } mDismissAction = action; mCancelAction = cancelAction; } public boolean hasDismissActions() { return mDismissAction != null || mCancelAction != null; } public void cancelDismissAction() { setOnDismissAction(null, null); } @Override protected void onFinishInflate() { mSecurityContainer = findViewById(R.id.keyguard_security_container); mLockPatternUtils = new LockPatternUtils(mContext); mSecurityContainer.setLockPatternUtils(mLockPatternUtils); mSecurityContainer.setSecurityCallback(this); mSecurityContainer.showPrimarySecurityScreen(false); }
Called when the view needs to be shown.
/** * Called when the view needs to be shown. */
public void showPrimarySecurityScreen() { if (DEBUG) Log.d(TAG, "show()"); mSecurityContainer.showPrimarySecurityScreen(false); }
Show a string explaining why the security view needs to be solved.
Params:
/** * Show a string explaining why the security view needs to be solved. * * @param reason a flag indicating which string should be shown, see * {@link KeyguardSecurityView#PROMPT_REASON_NONE}, * {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}. */
public void showPromptReason(int reason) { mSecurityContainer.showPromptReason(reason); } public void showMessage(CharSequence message, int color) { mSecurityContainer.showMessage(message, color); } public void showErrorMessage(CharSequence message) { showMessage(message, Utils.getColorError(mContext)); }
Dismisses the keyguard by going to the next screen or making it gone.
Params:
  • targetUserId – a user that needs to be the foreground user at the dismissal completion.
Returns:True if the keyguard is done.
/** * Dismisses the keyguard by going to the next screen or making it gone. * @param targetUserId a user that needs to be the foreground user at the dismissal completion. * @return True if the keyguard is done. */
public boolean dismiss(int targetUserId) { return dismiss(false, targetUserId); } public boolean handleBackKey() { if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser()); return true; } return false; } protected KeyguardSecurityContainer getSecurityContainer() { return mSecurityContainer; } @Override public boolean dismiss(boolean authenticated, int targetUserId) { return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId); }
Authentication has happened and it's time to dismiss keyguard. This function should clean up and inform KeyguardViewMediator.
Params:
  • strongAuth – whether the user has authenticated with strong authentication like pattern, password or PIN but not by trust agents or fingerprint
  • targetUserId – a user that needs to be the foreground user at the dismissal completion.
/** * Authentication has happened and it's time to dismiss keyguard. This function * should clean up and inform KeyguardViewMediator. * * @param strongAuth whether the user has authenticated with strong authentication like * pattern, password or PIN but not by trust agents or fingerprint * @param targetUserId a user that needs to be the foreground user at the dismissal completion. */
@Override public void finish(boolean strongAuth, int targetUserId) { // If there's a pending runnable because the user interacted with a widget // and we're leaving keyguard, then run it. boolean deferKeyguardDone = false; if (mDismissAction != null) { deferKeyguardDone = mDismissAction.onDismiss(); mDismissAction = null; mCancelAction = null; } if (mViewMediatorCallback != null) { if (deferKeyguardDone) { mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId); } else { mViewMediatorCallback.keyguardDone(strongAuth, targetUserId); } } } @Override public void reset() { mViewMediatorCallback.resetKeyguard(); } public void resetSecurityContainer() { mSecurityContainer.reset(); } @Override public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { if (mViewMediatorCallback != null) { mViewMediatorCallback.setNeedsInput(needsInput); } } public CharSequence getAccessibilityTitleForCurrentMode() { return mSecurityContainer.getTitle(); } public void userActivity() { if (mViewMediatorCallback != null) { mViewMediatorCallback.userActivity(); } }
Called when the Keyguard is not actively shown anymore on the screen.
/** * Called when the Keyguard is not actively shown anymore on the screen. */
public void onPause() { if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); mSecurityContainer.showPrimarySecurityScreen(true); mSecurityContainer.onPause(); clearFocus(); }
Called when the Keyguard is actively shown on the screen.
/** * Called when the Keyguard is actively shown on the screen. */
public void onResume() { if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); requestFocus(); }
Starts the animation when the Keyguard gets shown.
/** * Starts the animation when the Keyguard gets shown. */
public void startAppearAnimation() { mSecurityContainer.startAppearAnimation(); } public void startDisappearAnimation(Runnable finishRunnable) { if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) { finishRunnable.run(); } }
Called before this view is being removed.
/** * Called before this view is being removed. */
public void cleanUp() { getSecurityContainer().onPause(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (interceptMediaKey(event)) { return true; } return super.dispatchKeyEvent(event); }
Allows the media keys to work when the keyguard is showing. The media keys should be of no interest to the actual keyguard view(s), so intercepting them here should not be of any harm.
Params:
  • event – The key event
Returns:whether the event was consumed as a media key.
/** * Allows the media keys to work when the keyguard is showing. * The media keys should be of no interest to the actual keyguard view(s), * so intercepting them here should not be of any harm. * @param event The key event * @return whether the event was consumed as a media key. */
public boolean interceptMediaKey(KeyEvent event) { final int keyCode = event.getKeyCode(); if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: /* Suppress PLAY/PAUSE toggle when phone is ringing or * in-call to avoid music playback */ if (mTelephonyManager == null) { mTelephonyManager = (TelephonyManager) getContext().getSystemService( Context.TELEPHONY_SERVICE); } if (mTelephonyManager != null && mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { return true; // suppress key event } case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_STOP: case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { handleMediaKeyEvent(event); return true; } case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { if (KEYGUARD_MANAGES_VOLUME) { synchronized (this) { if (mAudioManager == null) { mAudioManager = (AudioManager) getContext().getSystemService( Context.AUDIO_SERVICE); } } // Volume buttons should only function for music (local or remote). // TODO: Actually handle MUTE. mAudioManager.adjustSuggestedStreamVolume( keyCode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER /* direction */, AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); // Don't execute default volume behavior return true; } else { return false; } } } } else if (event.getAction() == KeyEvent.ACTION_UP) { switch (keyCode) { case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MEDIA_STOP: case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { handleMediaKeyEvent(event); return true; } } } return false; } private void handleMediaKeyEvent(KeyEvent keyEvent) { synchronized (this) { if (mAudioManager == null) { mAudioManager = (AudioManager) getContext().getSystemService( Context.AUDIO_SERVICE); } } mAudioManager.dispatchMediaKeyEvent(keyEvent); } @Override public void dispatchSystemUiVisibilityChanged(int visibility) { super.dispatchSystemUiVisibilityChanged(visibility); if (!(mContext instanceof Activity)) { setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); } }
In general, we enable unlocking the insecure keyguard with the menu key. However, there are some cases where we wish to disable it, notably when the menu button placement or technology is prone to false positives.
Returns:true if the menu key should be enabled
/** * In general, we enable unlocking the insecure keyguard with the menu key. However, there are * some cases where we wish to disable it, notably when the menu button placement or technology * is prone to false positives. * * @return true if the menu key should be enabled */
private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; public boolean shouldEnableMenuKey() { final Resources res = getResources(); final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); return !configDisabled || isTestHarness || fileOverride; } public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { mViewMediatorCallback = viewMediatorCallback; // Update ViewMediator with the current input method requirements mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput()); } public void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; mSecurityContainer.setLockPatternUtils(utils); } public SecurityMode getSecurityMode() { return mSecurityContainer.getSecurityMode(); } public SecurityMode getCurrentSecurityMode() { return mSecurityContainer.getCurrentSecurityMode(); } }