blob: 938ed2329ffde5d49b48563197bc4eb2633de857 [file] [log] [blame]
/*
* Copyright (C) 2006 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.server.policy;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
import static android.view.KeyEvent.KEYCODE_HOME;
import static android.view.KeyEvent.KEYCODE_POWER;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_LOCK;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_NONE;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_SLEEP;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION;
import static com.android.server.wm.WindowManagerPolicyProto.ROTATION;
import static com.android.server.wm.WindowManagerPolicyProto.ROTATION_MODE;
import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
import android.accessibilityservice.AccessibilityService;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IUiModeManager;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.session.MediaSessionLegacyHelper;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeviceIdleManager;
import android.os.FactoryTest;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableBoolean;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.IDisplayFoldListener;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyCharacterMap.FallbackAction;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillManagerInternal;
import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.display.BrightnessUtils;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.policy.LogDecelerateInterpolator;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.ExtconStateObserver;
import com.android.server.ExtconUEventObserver;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.UiThread;
import com.android.server.input.InputManagerInternal;
import com.android.server.input.KeyboardMetricsCollector;
import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vibrator.HapticFeedbackVibrationProvider;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
/**
* WindowManagerPolicy implementation for the Android phone UI. This
* introduces a new method suffix, Lp, for an internal lock of the
* PhoneWindowManager. This is used to protect some internal state, and
* can be acquired with either the Lw and Li lock held, so has the restrictions
* of both of those when held.
*/
public class PhoneWindowManager implements WindowManagerPolicy {
static final String TAG = "WindowManager";
static final boolean localLOGV = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_KEYGUARD = false;
static final boolean DEBUG_WAKEUP = false;
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
// No longer recommended for desk docks;
static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
// Whether to allow devices placed in vr headset viewers to have an alternative Home intent.
static final boolean ENABLE_VR_HEADSET_HOME_CAPTURE = true;
// --------- Key behavior definitions below. ---------
// NOTE: When updating the valid range of any of these behaviors, and if that behavior has a
// Settings key associated to it for dynamic update, update its valid Settings value range in
// android.provider.settings.validators.GlobalSettingsValidators.
// must match: config_shortPressOnPowerBehavior in config.xml
// The config value can be overridden using Settings.Global.POWER_BUTTON_SHORT_PRESS
static final int SHORT_PRESS_POWER_NOTHING = 0;
static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
static final int SHORT_PRESS_POWER_GO_HOME = 4;
static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7;
// must match: config_LongPressOnPowerBehavior in config.xml
// The config value can be overridden using Settings.Global.POWER_BUTTON_LONG_PRESS
static final int LONG_PRESS_POWER_NOTHING = 0;
static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
static final int LONG_PRESS_POWER_GO_TO_VOICE_ASSIST = 4;
static final int LONG_PRESS_POWER_ASSISTANT = 5; // Settings.Secure.ASSISTANT
// must match: config_veryLongPresOnPowerBehavior in config.xml
// The config value can be overridden using Settings.Global.POWER_BUTTON_VERY_LONG_PRESS
static final int VERY_LONG_PRESS_POWER_NOTHING = 0;
static final int VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
// must match: config_keyChordPowerVolumeUp in config.xml
static final int POWER_VOLUME_UP_BEHAVIOR_NOTHING = 0;
static final int POWER_VOLUME_UP_BEHAVIOR_MUTE = 1;
static final int POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS = 2;
// must match: config_doublePressOnPowerBehavior in config.xml
// The config value can be overridden using Settings.Global.POWER_BUTTON_DOUBLE_PRESS and/or
// Settings.Global.POWER_BUTTON_TRIPLE_PRESS
static final int MULTI_PRESS_POWER_NOTHING = 0;
static final int MULTI_PRESS_POWER_THEATER_MODE = 1;
static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2;
static final int MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY = 3;
// must match: config_longPressOnBackBehavior in config.xml
static final int LONG_PRESS_BACK_NOTHING = 0;
static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;
// must match: config_longPressOnHomeBehavior in config.xml
static final int LONG_PRESS_HOME_NOTHING = 0;
static final int LONG_PRESS_HOME_ALL_APPS = 1;
static final int LONG_PRESS_HOME_ASSIST = 2;
static final int LONG_PRESS_HOME_NOTIFICATION_PANEL = 3;
static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_NOTIFICATION_PANEL;
// must match: config_doubleTapOnHomeBehavior in config.xml
static final int DOUBLE_TAP_HOME_NOTHING = 0;
static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1;
static final int DOUBLE_TAP_HOME_PIP_MENU = 2;
static final int SHORT_PRESS_WINDOW_NOTHING = 0;
static final int SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE = 1;
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
// must match: config_shortPressOnSettingsBehavior in config.xml
static final int SHORT_PRESS_SETTINGS_NOTHING = 0;
static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1;
static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
static final int PENDING_KEY_NULL = -1;
// Must match: config_shortPressOnStemPrimaryBehavior in config.xml
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS
static final int SHORT_PRESS_PRIMARY_NOTHING = 0;
static final int SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS = 1;
static final int SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY = 2;
// Must match: config_longPressOnStemPrimaryBehavior in config.xml
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS
static final int LONG_PRESS_PRIMARY_NOTHING = 0;
static final int LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT = 1;
// Must match: config_doublePressOnStemPrimaryBehavior in config.xml
//The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS
static final int DOUBLE_PRESS_PRIMARY_NOTHING = 0;
static final int DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP = 1;
// Must match: config_triplePressOnStemPrimaryBehavior in config.xml
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS
static final int TRIPLE_PRESS_PRIMARY_NOTHING = 0;
static final int TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY = 1;
// Must match: config_searchKeyBehavior in config.xml
static final int SEARCH_BEHAVIOR_DEFAULT_SEARCH = 0;
static final int SEARCH_BEHAVIOR_TARGET_ACTIVITY = 1;
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
static public final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn";
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
/**
* Keyguard stuff
*/
private boolean mKeyguardDrawnOnce;
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
/**
* Extra time for additional SystemUI animations.
* <p>Since legacy apps can add Toast windows directly instead of using Toast APIs,
* {@link DisplayPolicy} ensures that the window manager removes toast windows after
* TOAST_WINDOW_TIMEOUT. We increase this timeout by TOAST_WINDOW_ANIM_BUFFER to account for
* SystemUI's in/out toast animations, so that the toast text is still shown for a minimum
* of 3.5 seconds and the animations are finished before window manager removes the window.
*/
public static final int TOAST_WINDOW_ANIM_BUFFER = 600;
/**
* Amount of time (in milliseconds) a toast window can be shown before it's automatically
* removed by window manager.
*/
public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER;
/**
* Action for launching assistant in retail mode
*/
private static final String ACTION_VOICE_ASSIST_RETAIL =
"android.intent.action.VOICE_ASSIST_RETAIL";
/**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
* where the window manager is calling in with its own lock held.)
*/
private final Object mLock = new Object();
/** List of {@link ScreenOnListener}s which do not belong to the default display. */
private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>();
Context mContext;
WindowManagerFuncs mWindowManagerFuncs;
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
ActivityManagerInternal mActivityManagerInternal;
IActivityManager mActivityManagerService;
ActivityTaskManagerInternal mActivityTaskManagerInternal;
AutofillManagerInternal mAutofillManagerInternal;
InputManager mInputManager;
InputManagerInternal mInputManagerInternal;
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
StatusBarManagerInternal mStatusBarManagerInternal;
AudioManagerInternal mAudioManagerInternal;
SensorPrivacyManager mSensorPrivacyManager;
DisplayManager mDisplayManager;
DisplayManagerInternal mDisplayManagerInternal;
UserManagerInternal mUserManagerInternal;
private WallpaperManagerInternal mWallpaperManagerInternal;
boolean mPreloadedRecentApps;
final Object mServiceAcquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
AccessibilityManager mAccessibilityManager;
AccessibilityManagerInternal mAccessibilityManagerInternal;
BurnInProtectionHelper mBurnInProtectionHelper;
private DisplayFoldController mDisplayFoldController;
AppOpsManager mAppOpsManager;
PackageManager mPackageManager;
SideFpsEventHandler mSideFpsEventHandler;
LockPatternUtils mLockPatternUtils;
private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
private boolean mHasFeatureHdmiCec;
// Assigned on main thread, accessed on UI thread
volatile VrManagerInternal mVrManagerInternal;
/** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
boolean mEnableShiftMenuBugReports = false;
/** Controller that supports enabling an AccessibilityService by holding down the volume keys */
private AccessibilityShortcutController mAccessibilityShortcutController;
private TalkbackShortcutController mTalkbackShortcutController;
boolean mSafeMode;
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
// This is for car dock and this is updated from resource.
private boolean mEnableCarDockHomeCapture = true;
boolean mBootMessageNeedsHiding;
volatile boolean mBootAnimationDismissable;
private KeyguardServiceDelegate mKeyguardDelegate;
private boolean mKeyguardBound;
final DrawnListener mKeyguardDrawnCallback = new DrawnListener() {
@Override
public void onDrawn() {
if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn.");
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
}
};
private Supplier<GlobalActions> mGlobalActionsFactory;
private GlobalActions mGlobalActions;
private Handler mHandler;
// FIXME This state is shared between the input reader and handler thread.
// Technically it's broken and buggy but it has been like this for many years
// and we have not yet seen any problems. Someday we'll rewrite this logic
// so that only one thread is involved in handling input policy. Unfortunately
// it's on a critical path for power management so we can't just post the work to the
// handler thread. We'll need to resolve this someday by teaching the input dispatcher
// to hold wakelocks during dispatch and eliminating the critical path.
volatile boolean mPowerKeyHandled;
volatile boolean mBackKeyHandled;
volatile boolean mEndCallKeyHandled;
volatile boolean mCameraGestureTriggered;
volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
/**
* {@code true} if the device is entering a low-power state; {@code false otherwise}.
*
* <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state
* of the {@link #mDefaultDisplay default display} versus the power state of the entire device.
*/
volatile boolean mDeviceGoingToSleep;
/**
* {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to
* enter a low-power state; {@code false otherwise}.
*
* <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire
* device versus the power state of the {@link #mDefaultDisplay default display}.
*/
// TODO(b/178103325): Track sleep/requested sleep for every display.
volatile boolean mRequestedOrSleepingDefaultDisplay;
volatile boolean mRecentsVisible;
volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
volatile boolean mPictureInPictureVisible;
volatile private boolean mDismissImeOnBackKeyPressed;
// Used to hold the last user key used to wake the device. This helps us prevent up events
// from being passed to the foregrounded app without a corresponding down event
volatile int mPendingWakeKey = PENDING_KEY_NULL;
int mRecentAppsHeldModifiers;
int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT;
boolean mHaveBuiltInKeyboard;
boolean mSystemReady;
boolean mSystemBooted;
HdmiControl mHdmiControl;
IUiModeManager mUiModeManager;
int mUiMode;
boolean mWakeGestureEnabledSetting;
MyWakeGestureListener mWakeGestureListener;
int mLidKeyboardAccessibility;
int mLidNavigationAccessibility;
int mShortPressOnPowerBehavior;
private boolean mShouldEarlyShortPressOnPower;
boolean mShouldEarlyShortPressOnStemPrimary;
int mLongPressOnPowerBehavior;
long mLongPressOnPowerAssistantTimeoutMs;
int mVeryLongPressOnPowerBehavior;
int mDoublePressOnPowerBehavior;
ComponentName mPowerDoublePressTargetActivity;
int mTriplePressOnPowerBehavior;
int mLongPressOnBackBehavior;
int mShortPressOnSleepBehavior;
int mShortPressOnWindowBehavior;
int mPowerVolUpBehavior;
boolean mStylusButtonsEnabled = true;
boolean mKidsModeEnabled;
boolean mHasSoftInput = false;
boolean mUseTvRouting;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
MetricsLogger mLogger;
boolean mWakeOnDpadKeyPress;
boolean mWakeOnAssistKeyPress;
boolean mWakeOnBackKeyPress;
boolean mSilenceRingerOnSleepKey;
long mWakeUpToLastStateTimeout;
int mSearchKeyBehavior;
ComponentName mSearchKeyTargetActivity;
// Key Behavior - Stem Primary
private int mShortPressOnStemPrimaryBehavior;
private int mDoublePressOnStemPrimaryBehavior;
private int mTriplePressOnStemPrimaryBehavior;
private int mLongPressOnStemPrimaryBehavior;
private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
private boolean mHandleVolumeKeysInWM;
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
Intent mVrHeadsetHomeIntent;
boolean mPendingMetaAction;
boolean mPendingCapsLockToggle;
// support for activating the lock screen while the screen is on
private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>();
int mLockScreenTimeout;
boolean mLockScreenTimerActive;
// Behavior of ENDCALL Button. (See Settings.System.END_BUTTON_BEHAVIOR.)
int mEndcallBehavior;
// Behavior of POWER button while in-call and screen on.
// (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
int mIncallPowerBehavior;
// Behavior of Back button while in-call and screen on
int mIncallBackBehavior;
// Whether system navigation keys are enabled
boolean mSystemNavigationKeysEnabled;
// TODO(b/111361251): Remove default when the dependencies are multi-display ready.
Display mDefaultDisplay;
DisplayRotation mDefaultDisplayRotation;
DisplayPolicy mDefaultDisplayPolicy;
// What we do when the user long presses on home
int mLongPressOnHomeBehavior;
// What we do when the user double-taps on home
int mDoubleTapOnHomeBehavior;
// What we do when the user presses on settings
int mShortPressOnSettingsBehavior;
// Must match config_primaryShortPressTargetActivity in config.xml
ComponentName mPrimaryShortPressTargetActivity;
// Whether to lock the device after the next dreaming transition has finished.
private boolean mLockAfterDreamingTransitionFinished;
// Allowed theater mode wake actions
private boolean mAllowTheaterModeWakeFromKey;
private boolean mAllowTheaterModeWakeFromPowerKey;
private boolean mAllowTheaterModeWakeFromMotion;
private boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming;
private boolean mAllowTheaterModeWakeFromCameraLens;
private boolean mAllowTheaterModeWakeFromLidSwitch;
private boolean mAllowTheaterModeWakeFromWakeGesture;
// If true, the power button long press behavior will be invoked even if the default display is
// non-interactive. If false, the power button long press behavior will be skipped if the
// default display is non-interactive.
private boolean mSupportLongPressPowerWhenNonInteractive;
// If true, the power button short press behavior will be always invoked as long as the default
// display is on, even if the display is not interactive. If false, the power button short press
// behavior will be skipped if the default display is non-interactive.
private boolean mSupportShortPressPowerWhenDefaultDisplayOn;
// Whether to go to sleep entering theater mode from power button
private boolean mGoToSleepOnButtonPressTheaterMode;
// Screenshot trigger states
// Increase the chord delay when taking a screenshot from the keyguard
private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f;
// Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
int mRingerToggleChord = VOLUME_HUSH_OFF;
private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000;
/* The number of steps between min and max brightness */
private static final int BRIGHTNESS_STEPS = 10;
SettingsObserver mSettingsObserver;
ModifierShortcutManager mModifierShortcutManager;
/** Currently fully consumed key codes per device */
private final SparseArray<Set<Integer>> mConsumedKeysForDevice = new SparseArray<>();
PowerManager.WakeLock mBroadcastWakeLock;
PowerManager.WakeLock mPowerKeyWakeLock;
boolean mHavePendingMediaKeyRepeatWithWakeLock;
private int mCurrentUserId;
// Maps global key codes to the components that will handle them.
private GlobalKeyManager mGlobalKeyManager;
// Fallback actions by key code.
private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions =
new SparseArray<KeyCharacterMap.FallbackAction>();
private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator
= new LogDecelerateInterpolator(100, 0);
private final DeferredKeyActionExecutor mDeferredKeyActionExecutor =
new DeferredKeyActionExecutor();
private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;
private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
private KeyCombinationManager mKeyCombinationManager;
private SingleKeyGestureDetector mSingleKeyGestureDetector;
private GestureLauncherService mGestureLauncherService;
private ButtonOverridePermissionChecker mButtonOverridePermissionChecker;
private boolean mLockNowPending = false;
// Timeout for showing the keyguard after the screen is on, in case no "ready" is received.
private int mKeyguardDrawnTimeout = 1000;
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
private static final int MSG_KEYGUARD_DRAWN_TIMEOUT = 6;
private static final int MSG_WINDOW_MANAGER_DRAWN_COMPLETE = 7;
private static final int MSG_DISPATCH_SHOW_RECENTS = 9;
private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10;
private static final int MSG_HIDE_BOOT_MESSAGE = 11;
private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
private static final int MSG_SCREENSHOT_CHORD = 16;
private static final int MSG_ACCESSIBILITY_SHORTCUT = 17;
private static final int MSG_BUGREPORT_TV = 18;
private static final int MSG_ACCESSIBILITY_TV = 19;
private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20;
private static final int MSG_SYSTEM_KEY_PRESS = 21;
private static final int MSG_HANDLE_ALL_APPS = 22;
private static final int MSG_LAUNCH_ASSIST = 23;
private static final int MSG_RINGER_TOGGLE_CHORD = 24;
private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25;
private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26;
private static final int MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE = 27;
private class PolicyHandler extends Handler {
private PolicyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK:
dispatchMediaKeyWithWakeLock((KeyEvent)msg.obj);
break;
case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
break;
case MSG_DISPATCH_SHOW_RECENTS:
showRecentApps(false);
break;
case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS:
showGlobalActionsInternal();
break;
case MSG_KEYGUARD_DRAWN_COMPLETE:
if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");
finishKeyguardDrawn();
break;
case MSG_KEYGUARD_DRAWN_TIMEOUT:
Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete");
finishKeyguardDrawn();
break;
case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
final int displayId = msg.arg1;
if (DEBUG_WAKEUP) Slog.w(TAG, "All windows drawn on display " + displayId);
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
finishWindowsDrawn(displayId);
break;
case MSG_HIDE_BOOT_MESSAGE:
handleHideBootMessage();
break;
case MSG_LAUNCH_ASSIST:
final int deviceId = msg.arg1;
final Long eventTime = (Long) msg.obj;
launchAssistAction(null /* hint */, deviceId, eventTime,
AssistUtils.INVOCATION_TYPE_ASSIST_BUTTON);
break;
case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
launchVoiceAssistWithWakeLock();
break;
case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
showPictureInPictureMenuInternal();
break;
case MSG_ACCESSIBILITY_SHORTCUT:
accessibilityShortcutActivated();
break;
case MSG_BUGREPORT_TV:
requestBugreportForTv();
break;
case MSG_ACCESSIBILITY_TV:
if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
accessibilityShortcutActivated();
}
break;
case MSG_DISPATCH_BACK_KEY_TO_AUTOFILL:
mAutofillManagerInternal.onBackKeyPressed();
break;
case MSG_SYSTEM_KEY_PRESS:
KeyEvent event = (KeyEvent) msg.obj;
sendSystemKeyToStatusBar(event);
event.recycle();
break;
case MSG_HANDLE_ALL_APPS:
launchAllAppsAction();
break;
case MSG_RINGER_TOGGLE_CHORD:
handleRingerChordGesture();
break;
case MSG_SCREENSHOT_CHORD:
handleScreenShot(msg.arg1);
break;
case MSG_SWITCH_KEYBOARD_LAYOUT:
SwitchKeyboardLayoutMessageObject object =
(SwitchKeyboardLayoutMessageObject) msg.obj;
handleSwitchKeyboardLayout(object.keyEvent, object.direction,
object.focusedToken);
break;
case MSG_LOG_KEYBOARD_SYSTEM_EVENT:
handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj);
break;
case MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE:
final int keyCode = msg.arg1;
final long downTime = (Long) msg.obj;
mDeferredKeyActionExecutor.setActionsExecutable(keyCode, downTime);
break;
}
}
}
private UEventObserver mHDMIObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
mDefaultDisplayPolicy.setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
}
};
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
// Observe all users' changes
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.END_BUTTON_BEHAVIOR), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_SHORT_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_DOUBLE_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_TRIPLE_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_LONG_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.KEY_CHORD_POWER_VOLUME_UP), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.STYLUS_BUTTONS_ENABLED), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.NAV_BAR_KIDS_MODE), false, this,
UserHandle.USER_ALL);
updateSettings();
}
@Override public void onChange(boolean selfChange) {
updateSettings();
}
}
class MyWakeGestureListener extends WakeGestureListener {
MyWakeGestureListener(Context context, Handler handler) {
super(context, handler);
}
@Override
public void onWakeUp() {
synchronized (mLock) {
if (shouldEnableWakeGestureLp()) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Wake Up");
wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
}
}
}
}
private record SwitchKeyboardLayoutMessageObject(KeyEvent keyEvent, IBinder focusedToken,
int direction) {
}
final IPersistentVrStateCallbacks mPersistentVrModeListener =
new IPersistentVrStateCallbacks.Stub() {
@Override
public void onPersistentVrStateChanged(boolean enabled) {
mDefaultDisplayPolicy.setPersistentVrModeEnabled(enabled);
}
};
private void handleRingerChordGesture() {
if (mRingerToggleChord == VOLUME_HUSH_OFF) {
return;
}
getAudioManagerInternal();
mAudioManagerInternal.silenceRingerModeInternal("volume_hush");
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.HUSH_GESTURE_USED, 1);
mLogger.action(MetricsProto.MetricsEvent.ACTION_HUSH_GESTURE, mRingerToggleChord);
}
IStatusBarService getStatusBarService() {
synchronized (mServiceAcquireLock) {
if (mStatusBarService == null) {
mStatusBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService("statusbar"));
}
return mStatusBarService;
}
}
StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mStatusBarManagerInternal == null) {
mStatusBarManagerInternal =
LocalServices.getService(StatusBarManagerInternal.class);
}
return mStatusBarManagerInternal;
}
}
AudioManagerInternal getAudioManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mAudioManagerInternal == null) {
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
}
return mAudioManagerInternal;
}
}
AccessibilityManagerInternal getAccessibilityManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mAccessibilityManagerInternal == null) {
mAccessibilityManagerInternal =
LocalServices.getService(AccessibilityManagerInternal.class);
}
return mAccessibilityManagerInternal;
}
}
// returns true if the key was handled and should not be passed to the user
private boolean backKeyPress() {
mLogger.count("key_back_press", 1);
// Cache handled state
boolean handled = mBackKeyHandled;
if (mHasFeatureWatch) {
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing back while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
// It should not prevent navigating away
return false;
} else if (
(mIncallBackBehavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall()) {
// Otherwise, if "Back button ends call" is enabled,
// the Back button will hang up any current active call.
return telecomManager.endCall();
}
}
}
if (mAutofillManagerInternal != null) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
}
return handled;
}
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.acquire();
}
mWindowManagerFuncs.onPowerKeyDown(interactive);
// Stop ringing or end call if configured to do so when power is pressed.
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
// Inform the StatusBar; but do not allow it to consume the event.
sendSystemKeyToStatusBarAsync(event);
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = mPowerKeyHandled || hungUp
|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (!interactive) {
wakeUpFromPowerKey(event.getDownTime());
}
} else {
// handled by another power key policy.
if (mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
Slog.d(TAG, "Skip power key gesture for other policy has handled it.");
mSingleKeyGestureDetector.reset();
}
}
}
private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
// Inform the StatusBar; but do not allow it to consume the event.
sendSystemKeyToStatusBarAsync(event);
final boolean handled = canceled || mPowerKeyHandled;
if (!handled) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
// Abort possibly stuck animations only when power key up without long press case.
mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
}
}
finishPowerKeyPress();
}
private void finishPowerKeyPress() {
mPowerKeyHandled = false;
if (mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.release();
}
}
private void powerPress(long eventTime, int count, int displayId) {
// SideFPS still needs to know about suppressed power buttons, in case it needs to block
// an auth attempt.
if (count == 1) {
mSideFpsEventHandler.notifyPowerPressed();
}
if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
return;
}
final boolean interactive = mDefaultDisplayPolicy.isAwake();
Slog.d(
TAG,
"powerPress: eventTime="
+ eventTime
+ " interactive="
+ interactive
+ " count="
+ count
+ " mShortPressOnPowerBehavior="
+ mShortPressOnPowerBehavior);
if (count == 2) {
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
Slog.d(TAG, "No behavior defined for power press count " + count);
} else if (count == 1 && shouldHandleShortPressPowerAction(interactive, eventTime)) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
sleepDefaultDisplayFromPowerButton(eventTime, 0);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
sleepDefaultDisplayFromPowerButton(eventTime,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
if (sleepDefaultDisplayFromPowerButton(eventTime,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
launchHomeFromHotKey(DEFAULT_DISPLAY);
}
break;
case SHORT_PRESS_POWER_GO_HOME:
shortPressPowerGoHome();
break;
case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
if (mDismissImeOnBackKeyPressed) {
// TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
InputMethodManagerInternal.get().hideAllInputMethods(
SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, displayId);
} else {
shortPressPowerGoHome();
}
break;
}
case SHORT_PRESS_POWER_LOCK_OR_SLEEP: {
if (mKeyguardDelegate == null || !mKeyguardDelegate.hasKeyguard()
|| !mKeyguardDelegate.isSecure(mCurrentUserId) || keyguardOn()) {
sleepDefaultDisplayFromPowerButton(eventTime, 0);
} else {
lockNow(null /*options*/);
}
break;
}
case SHORT_PRESS_POWER_DREAM_OR_SLEEP: {
attemptToDreamFromShortPowerButtonPress(
true,
() -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
break;
}
}
}
}
private boolean shouldHandleShortPressPowerAction(boolean interactive, long eventTime) {
if (mSupportShortPressPowerWhenDefaultDisplayOn) {
final boolean defaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
final boolean beganFromDefaultDisplayOn =
mSingleKeyGestureDetector.beganFromDefaultDisplayOn();
if (!defaultDisplayOn || !beganFromDefaultDisplayOn) {
Slog.v(
TAG,
"Ignoring short press of power button because the default display is not"
+ " on. defaultDisplayOn="
+ defaultDisplayOn
+ ", beganFromDefaultDisplayOn="
+ beganFromDefaultDisplayOn);
return false;
}
return true;
}
final boolean beganFromNonInteractive = mSingleKeyGestureDetector.beganFromNonInteractive();
if (!interactive || beganFromNonInteractive) {
Slog.v(
TAG,
"Ignoring short press of power button because the device is not interactive."
+ " interactive="
+ interactive
+ ", beganFromNonInteractive="
+ beganFromNonInteractive);
return false;
}
if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
Slog.i(
TAG,
"Suppressing power key because the user is interacting with the "
+ "fingerprint sensor");
return false;
}
return true;
}
/**
* Attempt to dream from a power button press.
*
* @param isScreenOn Whether the screen is currently on.
* @param noDreamAction The action to perform if dreaming is not possible.
*/
private void attemptToDreamFromShortPowerButtonPress(
boolean isScreenOn, Runnable noDreamAction) {
if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP) {
noDreamAction.run();
return;
}
final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
Slog.d(TAG, "Can't start dreaming when attempting to dream from short power"
+ " press (isScreenOn=" + isScreenOn + ")");
noDreamAction.run();
return;
}
synchronized (mLock) {
// If the setting to lock instantly on power button press is true, then set the flag to
// lock after the dream transition has finished.
mLockAfterDreamingTransitionFinished =
mLockPatternUtils.getPowerButtonInstantlyLocks(mCurrentUserId);
}
dreamManagerInternal.requestDream();
}
/**
* Sends the default display to sleep as a result of a power button press.
*
* @return {@code true} if the device was sent to sleep, {@code false} if the device did not
* sleep.
*/
private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
// Before we actually go to sleep, we check the last wakeup reason.
// If the device very recently woke up from a gesture (like user lifting their device)
// then ignore the sleep instruction. This is because users have developed
// a tendency to hit the power button immediately when they pick up their device, and we
// don't want to put the device back to sleep in those cases.
final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();
if (lastWakeUp != null && (lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE
|| lastWakeUp.wakeReason == PowerManager.WAKE_REASON_LIFT
|| lastWakeUp.wakeReason == PowerManager.WAKE_REASON_BIOMETRIC)) {
final long now = SystemClock.uptimeMillis();
if (mPowerButtonSuppressionDelayMillis > 0
&& (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {
Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "
+ (now - lastWakeUp.wakeTime) + "ms");
return false;
}
}
sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
return true;
}
private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
mRequestedOrSleepingDefaultDisplay = true;
mPowerManager.goToSleep(eventTime, reason, flags);
}
private void shortPressPowerGoHome() {
launchHomeFromHotKey(DEFAULT_DISPLAY, true /* awakenFromDreams */,
false /*respectKeyguard*/);
if (isKeyguardShowingAndNotOccluded()) {
// Notify keyguard so it can do any special handling for the power button since the
// device will not power off and only launch home.
mKeyguardDelegate.onShortPowerPressedGoHome();
}
}
private void powerMultiPressAction(long eventTime, boolean interactive, int behavior) {
switch (behavior) {
case MULTI_PRESS_POWER_NOTHING:
break;
case MULTI_PRESS_POWER_THEATER_MODE:
if (!isUserSetupComplete()) {
Slog.i(TAG, "Ignoring toggling theater mode - device not setup.");
break;
}
if (isTheaterModeEnabled()) {
Slog.i(TAG, "Toggling theater mode off.");
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0);
if (!interactive) {
wakeUpFromPowerKey(eventTime);
}
} else {
Slog.i(TAG, "Toggling theater mode on.");
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 1);
if (mGoToSleepOnButtonPressTheaterMode && interactive) {
sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
0);
}
}
break;
case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
Slog.i(TAG, "Starting brightness boost.");
if (!interactive) {
wakeUpFromPowerKey(eventTime);
}
mPowerManager.boostScreenBrightness(eventTime);
break;
case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
launchTargetActivityOnMultiPressPower();
break;
}
}
private void launchTargetActivityOnMultiPressPower() {
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing the double press power action.");
}
if (mPowerDoublePressTargetActivity != null) {
Intent intent = new Intent();
intent.setComponent(mPowerDoublePressTargetActivity);
ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
intent, /* flags= */0);
if (resolveInfo != null) {
final boolean keyguardActive =
mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
if (!keyguardActive) {
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
} else {
mKeyguardDelegate.dismissKeyguardToLaunch(intent);
}
} else {
Slog.e(TAG, "Could not resolve activity with : "
+ mPowerDoublePressTargetActivity.flattenToString()
+ " name.");
}
}
}
private int getLidBehavior() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LID_BEHAVIOR, LID_BEHAVIOR_NONE);
}
private int getMaxMultiPressPowerCount() {
// The actual max power button press count is 5
// (EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD), which is coming from
// GestureLauncherService.
// To speed up the handling of single-press of power button inside SingleKeyGestureDetector,
// however, we limit the max count to the number of button presses actually handled by the
// SingleKeyGestureDetector except for wearable devices, where we want to de-dup the double
// press gesture with the emergency gesture.
if (mHasFeatureWatch
&& GestureLauncherService.isEmergencyGestureSettingEnabled(
mContext, ActivityManager.getCurrentUser())) {
return 5;
}
if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
return 3;
}
if (mDoublePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
return 2;
}
return 1;
}
private void powerLongPress(long eventTime) {
final int behavior = getResolvedLongPressOnPowerBehavior();
Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
+ " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Global Actions");
showGlobalActions();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
// don't actually trigger the shutdown if we are running stability
// tests via monkey
if (ActivityManager.isUserAMonkey()) {
break;
}
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Shut Off");
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Go To Voice Assist");
// Some devices allow the voice assistant intent during setup (and use that intent
// to launch something else, like Settings). So we explicitly allow that via the
// config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.
launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);
break;
case LONG_PRESS_POWER_ASSISTANT:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
"Power - Long Press - Go To Assistant");
final int powerKeyDeviceId = INVALID_INPUT_DEVICE_ID;
launchAssistAction(null, powerKeyDeviceId, eventTime,
AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
break;
}
}
private void powerVeryLongPress() {
switch (mVeryLongPressOnPowerBehavior) {
case VERY_LONG_PRESS_POWER_NOTHING:
break;
case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Very Long Press - Show Global Actions");
showGlobalActions();
break;
}
}
private void backLongPress() {
mBackKeyHandled = true;
switch (mLongPressOnBackBehavior) {
case LONG_PRESS_BACK_NOTHING:
break;
case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
launchVoiceAssist(false /* allowDuringSetup */);
break;
}
}
private void accessibilityShortcutActivated() {
mAccessibilityShortcutController.performAccessibilityShortcut();
}
private void sleepPress() {
if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */,
true /*respectKeyguard*/);
}
}
private void sleepRelease(long eventTime) {
if (mSilenceRingerOnSleepKey) {
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null && telecomManager.isRinging()) {
telecomManager.silenceRinger();
Slog.i(TAG, "sleepRelease() silence ringer");
return;
}
}
switch (mShortPressOnSleepBehavior) {
case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
break;
}
}
private int getResolvedLongPressOnPowerBehavior() {
if (FactoryTest.isLongPressOnPowerOffEnabled()) {
return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
}
// If the config indicates the assistant behavior but the device isn't yet provisioned, show
// global actions instead.
if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_ASSISTANT && !isDeviceProvisioned()) {
return LONG_PRESS_POWER_GLOBAL_ACTIONS;
}
// If long press to launch assistant is disabled in settings, do nothing.
if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_GO_TO_VOICE_ASSIST
&& !isLongPressToAssistantEnabled(mContext)) {
return LONG_PRESS_POWER_NOTHING;
}
return mLongPressOnPowerBehavior;
}
private void stemPrimaryPress(int count) {
if (DEBUG_INPUT) {
Slog.d(TAG, "stemPrimaryPress: " + count);
}
if (count == 3) {
stemPrimaryTriplePressAction(mTriplePressOnStemPrimaryBehavior);
} else if (count == 2) {
stemPrimaryDoublePressAction(mDoublePressOnStemPrimaryBehavior);
} else if (count == 1) {
stemPrimarySinglePressAction(mShortPressOnStemPrimaryBehavior);
}
}
private void stemPrimarySinglePressAction(int behavior) {
if (DEBUG_INPUT) {
Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
}
if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;
final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (keyguardActive) {
// If keyguarded then notify the keyguard.
mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
return;
}
switch (behavior) {
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary short press action behavior.");
}
Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
allAppsIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
break;
case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
if (DEBUG_INPUT) {
Slog.d(
TAG,
"Executing stem primary short press action behavior for launching "
+ "target activity.");
}
if (mPrimaryShortPressTargetActivity != null) {
Intent targetActivityIntent = new Intent();
targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
ResolveInfo resolveInfo =
mContext.getPackageManager()
.resolveActivity(targetActivityIntent, /* flags= */ 0);
if (resolveInfo != null) {
targetActivityIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
startActivityAsUser(targetActivityIntent, UserHandle.CURRENT_OR_SELF);
} else {
Slog.wtf(
TAG,
"Could not resolve activity with : "
+ mPrimaryShortPressTargetActivity.flattenToString()
+ " name.");
}
} else {
Slog.wtf(
TAG,
"mPrimaryShortPressTargetActivity must not be null and correctly"
+ " specified");
}
break;
}
}
private void stemPrimaryDoublePressAction(int behavior) {
switch (behavior) {
case DOUBLE_PRESS_PRIMARY_NOTHING:
break;
case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary double press action behavior.");
}
final boolean keyguardActive = mKeyguardDelegate == null
? false
: mKeyguardDelegate.isShowing();
if (!keyguardActive) {
performStemPrimaryDoublePressSwitchToRecentTask();
}
break;
}
}
private void stemPrimaryTriplePressAction(int behavior) {
switch (behavior) {
case TRIPLE_PRESS_PRIMARY_NOTHING:
break;
case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary triple press action behavior.");
}
mTalkbackShortcutController.toggleTalkback(mCurrentUserId);
if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */
false, /* reason = */
"Stem primary - Triple Press - Toggle Accessibility");
}
break;
}
}
private void stemPrimaryLongPress(long eventTime) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary long press action behavior.");
}
switch (mLongPressOnStemPrimaryBehavior) {
case LONG_PRESS_PRIMARY_NOTHING:
break;
case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
final int stemPrimaryKeyDeviceId = INVALID_INPUT_DEVICE_ID;
launchAssistAction(
null,
stemPrimaryKeyDeviceId,
eventTime,
AssistUtils.INVOCATION_TYPE_UNKNOWN);
break;
}
}
/**
* Load most recent task (expect current task) and bring it to the front.
*/
void performStemPrimaryDoublePressSwitchToRecentTask() {
RecentTaskInfo targetTask = mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
if (targetTask == null) {
if (DEBUG_INPUT) {
Slog.w(TAG, "No recent task available! Show wallpaper.");
}
goHome();
return;
}
if (DEBUG_INPUT) {
Slog.d(
TAG,
"Starting task from recents. id="
+ targetTask.id
+ ", persistentId="
+ targetTask.persistentId
+ ", topActivity="
+ targetTask.topActivity
+ ", baseIntent="
+ targetTask.baseIntent);
}
try {
mActivityManagerService.startActivityFromRecents(targetTask.persistentId, null);
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Failed to start task " + targetTask.persistentId + " from recents", e);
}
}
private int getMaxMultiPressStemPrimaryCount() {
switch (mTriplePressOnStemPrimaryBehavior) {
case TRIPLE_PRESS_PRIMARY_NOTHING:
break;
case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
return 3;
}
break;
}
if (mDoublePressOnStemPrimaryBehavior != DOUBLE_PRESS_PRIMARY_NOTHING) {
return 2;
}
return 1;
}
private boolean hasLongPressOnPowerBehavior() {
return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
}
private boolean hasVeryLongPressOnPowerBehavior() {
return mVeryLongPressOnPowerBehavior != VERY_LONG_PRESS_POWER_NOTHING;
}
private boolean hasLongPressOnBackBehavior() {
return mLongPressOnBackBehavior != LONG_PRESS_BACK_NOTHING;
}
private boolean hasLongPressOnStemPrimaryBehavior() {
return mLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING;
}
private boolean hasStemPrimaryBehavior() {
return getMaxMultiPressStemPrimaryCount() > 1
|| hasLongPressOnStemPrimaryBehavior()
|| mShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
}
private void interceptScreenshotChord(int source, long pressDelay) {
mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
// arg2 is unused, but necessary to insure we call the correct method signature
// since the screenshot source is read from message.arg1
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source, 0),
pressDelay);
}
private void interceptAccessibilityShortcutChord() {
mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
getAccessibilityShortcutTimeout());
}
private void interceptRingerToggleChord() {
mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
getRingerToggleChordDelay());
}
private long getAccessibilityShortcutTimeout() {
final ViewConfiguration config = ViewConfiguration.get(mContext);
final boolean hasDialogShown = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, mCurrentUserId) != 0;
final boolean skipTimeoutRestriction =
Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION, 0,
mCurrentUserId) != 0;
// If users manually set the volume key shortcut for any accessibility service, the
// system would bypass the timeout restriction of the shortcut dialog.
return hasDialogShown || skipTimeoutRestriction
? config.getAccessibilityShortcutKeyTimeoutAfterConfirmation()
: config.getAccessibilityShortcutKeyTimeout();
}
private long getScreenshotChordLongPressDelay() {
long delayMs = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
if (mKeyguardDelegate.isShowing()) {
// Double the time it takes to take a screenshot from the keyguard
return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
}
return delayMs;
}
private long getRingerToggleChordDelay() {
// Always timeout like a tap
return ViewConfiguration.getTapTimeout();
}
private void cancelPendingScreenshotChordAction() {
mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
}
private void cancelPendingAccessibilityShortcutAction() {
mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
}
private void cancelPendingRingerToggleChordAction() {
mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
}
private final Runnable mEndCallLongPress = new Runnable() {
@Override
public void run() {
mEndCallKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
"End Call - Long Press - Show Global Actions");
showGlobalActionsInternal();
}
};
private void handleScreenShot(@WindowManager.ScreenshotSource int source) {
mDefaultDisplayPolicy.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, source);
}
@Override
public void showGlobalActions() {
mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
}
void showGlobalActionsInternal() {
if (mGlobalActions == null) {
mGlobalActions = mGlobalActionsFactory.get();
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
// since it took two seconds of long press to bring this up,
// poke the wake lock so they have some time to see the dialog.
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
private void cancelGlobalActionsAction() {
mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
}
boolean isDeviceProvisioned() {
return Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
}
@Override
public boolean isUserSetupComplete() {
boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
if (mHasFeatureLeanback) {
isSetupComplete &= isTvUserSetupComplete();
} else if (mHasFeatureAuto) {
isSetupComplete &= isAutoUserSetupComplete();
}
return isSetupComplete;
}
private boolean isAutoUserSetupComplete() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
"android.car.SETUP_WIZARD_IN_PROGRESS", 0, UserHandle.USER_CURRENT) == 0;
}
private boolean isTvUserSetupComplete() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
}
private void handleShortPressOnHome(KeyEvent event) {
logKeyboardSystemsEvent(event, KeyboardLogEvent.HOME);
// Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
final HdmiControl hdmiControl = getHdmiControl();
if (hdmiControl != null) {
hdmiControl.turnOnTv();
}
// If there's a dream running then use home to escape the dream
// but don't actually go home.
final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()) {
mDreamManagerInternal.stopDream(false /*immediate*/, "short press on home" /*reason*/);
return;
}
// Go home!
launchHomeFromHotKey(event.getDisplayId());
}
/**
* Creates an accessor to HDMI control service that performs the operation of
* turning on TV (optional) and switching input to us. If HDMI control service
* is not available or we're not a HDMI playback device, the operation is no-op.
* @return {@link HdmiControl} instance if available, null otherwise.
*/
private HdmiControl getHdmiControl() {
if (null == mHdmiControl) {
if (!mHasFeatureHdmiCec) {
return null;
}
HdmiControlManager manager = (HdmiControlManager) mContext.getSystemService(
Context.HDMI_CONTROL_SERVICE);
HdmiPlaybackClient client = null;
if (manager != null) {
client = manager.getPlaybackClient();
}
mHdmiControl = new HdmiControl(client);
}
return mHdmiControl;
}
private static class HdmiControl {
private final HdmiPlaybackClient mClient;
private HdmiControl(HdmiPlaybackClient client) {
mClient = client;
}
public void turnOnTv() {
if (mClient == null) {
return;
}
mClient.oneTouchPlay(new OneTouchPlayCallback() {
@Override
public void onComplete(int result) {
if (result != HdmiControlManager.RESULT_SUCCESS) {
Log.w(TAG, "One touch play failed: " + result);
}
}
});
}
}
private void launchAllAppsAction() {
Intent intent = new Intent(Intent.ACTION_ALL_APPS);
if (mHasFeatureLeanback) {
Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
intentLauncher.addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
PackageManager.MATCH_SYSTEM_ONLY,
mCurrentUserId);
if (resolveInfo != null) {
intent.setPackage(resolveInfo.activityInfo.packageName);
}
}
startActivityAsUser(intent, UserHandle.CURRENT);
}
private void launchAllAppsViaA11y() {
AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
if (accessibilityManager != null) {
accessibilityManager.performSystemAction(
AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
}
}
private void toggleNotificationPanel() {
IStatusBarService statusBarService = getStatusBarService();
if (isUserSetupComplete() && statusBarService != null) {
try {
statusBarService.togglePanel();
} catch (RemoteException e) {
// do nothing.
}
}
}
private void showSystemSettings() {
startActivityAsUser(new Intent(android.provider.Settings.ACTION_SETTINGS),
UserHandle.CURRENT_OR_SELF);
}
private void showPictureInPictureMenu(KeyEvent event) {
if (DEBUG_INPUT) Log.d(TAG, "showPictureInPictureMenu event=" + event);
mHandler.removeMessages(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
Message msg = mHandler.obtainMessage(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
msg.setAsynchronous(true);
msg.sendToTarget();
}
private void showPictureInPictureMenuInternal() {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.showPictureInPictureMenu();
}
}
/** A handler to handle home keys per display */
private class DisplayHomeButtonHandler {
private final int mDisplayId;
private boolean mHomePressed;
private boolean mHomeConsumed;
private KeyEvent mPendingHomeKeyEvent;
private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
if (mPendingHomeKeyEvent != null) {
handleShortPressOnHome(mPendingHomeKeyEvent);
mPendingHomeKeyEvent = null;
}
}
};
DisplayHomeButtonHandler(int displayId) {
mDisplayId = displayId;
}
boolean handleHomeButton(IBinder focusedToken, KeyEvent event) {
final boolean keyguardOn = keyguardOn();
final int repeatCount = event.getRepeatCount();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
if (DEBUG_INPUT) {
Log.d(TAG, String.format("handleHomeButton in display#%d mHomePressed = %b",
mDisplayId, mHomePressed));
}
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (!down) {
if (mDisplayId == DEFAULT_DISPLAY) {
cancelPreloadRecentApps();
}
mHomePressed = false;
if (mHomeConsumed) {
mHomeConsumed = false;
return true;
}
if (canceled) {
Log.i(TAG, "Ignoring HOME; event canceled.");
return true;
}
// Delay handling home if a double-tap is possible.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
// For the picture-in-picture menu, only add the delay if a pip is there.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
|| mPictureInPictureVisible) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mPendingHomeKeyEvent = event;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return true;
}
}
// Post to main thread to avoid blocking input pipeline.
mHandler.post(() -> handleShortPressOnHome(event));
return true;
}
final KeyInterceptionInfo info =
mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
if (info != null) {
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
if (info.layoutParamsType == TYPE_KEYGUARD_DIALOG
|| (info.layoutParamsType == TYPE_NOTIFICATION_SHADE
&& isKeyguardShowing())) {
// the "app" is keyguard, so give it the key
return false;
}
for (int t : WINDOW_TYPES_WHERE_HOME_DOESNT_WORK) {
if (info.layoutParamsType == t) {
// don't do anything, but also don't pass it to the app
return true;
}
}
}
// Remember that home is pressed and handle special actions.
if (repeatCount == 0) {
mHomePressed = true;
if (mPendingHomeKeyEvent != null) {
mPendingHomeKeyEvent = null;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
mHandler.post(() -> handleDoubleTapOnHome(event));
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
&& mDisplayId == DEFAULT_DISPLAY) {
preloadRecentApps();
}
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
// Post to main thread to avoid blocking input pipeline.
mHandler.post(() -> handleLongPressOnHome(event));
}
}
return true;
}
private void handleDoubleTapOnHome(KeyEvent event) {
if (mHomeConsumed) {
return;
}
switch (mDoubleTapOnHomeBehavior) {
case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
logKeyboardSystemsEvent(event, KeyboardLogEvent.APP_SWITCH);
mHomeConsumed = true;
toggleRecentApps();
break;
case DOUBLE_TAP_HOME_PIP_MENU:
mHomeConsumed = true;
showPictureInPictureMenuInternal();
break;
default:
Log.w(TAG, "No action or undefined behavior for double tap home: "
+ mDoubleTapOnHomeBehavior);
break;
}
}
private void handleLongPressOnHome(KeyEvent event) {
if (mHomeConsumed) {
return;
}
if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
return;
}
mHomeConsumed = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
"Home - Long Press");
switch (mLongPressOnHomeBehavior) {
case LONG_PRESS_HOME_ALL_APPS:
logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
launchAllAppsAction();
break;
case LONG_PRESS_HOME_ASSIST:
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
launchAssistAction(null, event.getDeviceId(), event.getEventTime(),
AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
break;
case LONG_PRESS_HOME_NOTIFICATION_PANEL:
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
toggleNotificationPanel();
break;
default:
Log.w(TAG, "Undefined long press on home behavior: "
+ mLongPressOnHomeBehavior);
break;
}
}
@Override
public String toString() {
return String.format("mDisplayId = %d, mHomePressed = %b", mDisplayId, mHomePressed);
}
}
/** A DisplayHomeButtonHandler map indexed by display id */
private final SparseArray<DisplayHomeButtonHandler> mDisplayHomeButtonHandlers =
new SparseArray<>();
private boolean isRoundWindow() {
return mContext.getResources().getConfiguration().isScreenRound();
}
@Override
public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
mDefaultDisplay = displayContentInfo.getDisplay();
mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
}
/** Point of injection for test dependencies. */
@VisibleForTesting
static class Injector {
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
private final Looper mLooper;
Injector(Context context, WindowManagerFuncs funcs, Looper looper) {
mContext = context;
mWindowManagerFuncs = funcs;
mLooper = looper;
}
Context getContext() {
return mContext;
}
WindowManagerFuncs getWindowManagerFuncs() {
return mWindowManagerFuncs;
}
Looper getLooper() {
return mLooper;
}
AccessibilityShortcutController getAccessibilityShortcutController(
Context context, Handler handler, int initialUserId) {
return new AccessibilityShortcutController(context, handler, initialUserId);
}
Supplier<GlobalActions> getGlobalActionsFactory() {
return () -> new GlobalActions(mContext, mWindowManagerFuncs);
}
KeyguardServiceDelegate getKeyguardServiceDelegate() {
return new KeyguardServiceDelegate(mContext,
new StateCallback() {
@Override
public void onTrustedChanged() {
mWindowManagerFuncs.notifyKeyguardTrustedChanged();
}
@Override
public void onShowingChanged() {
mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged();
}
});
}
IActivityManager getActivityManagerService() {
return ActivityManager.getService();
}
ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
return new ButtonOverridePermissionChecker();
}
TalkbackShortcutController getTalkbackShortcutController() {
return new TalkbackShortcutController(mContext);
}
}
/** {@inheritDoc} */
@Override
public void init(Context context, WindowManagerFuncs funcs) {
init(new Injector(context, funcs, Looper.myLooper()));
}
@VisibleForTesting
void init(Injector injector) {
mContext = injector.getContext();
mWindowManagerFuncs = injector.getWindowManagerFuncs();
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mActivityManagerService = injector.getActivityManagerService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mInputManager = mContext.getSystemService(InputManager.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mPackageManager = mContext.getPackageManager();
mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);
mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);
mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
mAccessibilityShortcutController = injector.getAccessibilityShortcutController(
mContext, new Handler(), mCurrentUserId);
mGlobalActionsFactory = injector.getGlobalActionsFactory();
mLockPatternUtils = new LockPatternUtils(mContext);
mLogger = new MetricsLogger();
mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
.createSleepTokenAcquirer("ScreenOff");
Resources res = mContext.getResources();
mWakeOnDpadKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
mWakeOnAssistKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);
mWakeOnBackKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress);
// Init display burn-in protection
boolean burnInProtectionEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableBurnInProtection);
// Allow a system property to override this. Used by developer settings.
boolean burnInProtectionDevMode =
SystemProperties.getBoolean("persist.debug.force_burn_in", false);
if (burnInProtectionEnabled || burnInProtectionDevMode) {
final int minHorizontal;
final int maxHorizontal;
final int minVertical;
final int maxVertical;
final int maxRadius;
if (burnInProtectionDevMode) {
minHorizontal = -8;
maxHorizontal = 8;
minVertical = -8;
maxVertical = -4;
maxRadius = (isRoundWindow()) ? 6 : -1;
} else {
Resources resources = mContext.getResources();
minHorizontal = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMinHorizontalOffset);
maxHorizontal = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMaxHorizontalOffset);
minVertical = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMinVerticalOffset);
maxVertical = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMaxVerticalOffset);
maxRadius = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMaxRadius);
}
mBurnInProtectionHelper = new BurnInProtectionHelper(
mContext, minHorizontal, maxHorizontal, minVertical, maxVertical, maxRadius);
}
mHandler = new PolicyHandler(injector.getLooper());
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
mUiMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mEnableCarDockHomeCapture = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableCarDockHomeLaunch);
mCarDockIntent = new Intent(Intent.ACTION_MAIN, null);
mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK);
mCarDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mDeskDockIntent = new Intent(Intent.ACTION_MAIN, null);
mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK);
mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mVrHeadsetHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mVrHeadsetHomeIntent.addCategory(Intent.CATEGORY_VR_HOME);
mVrHeadsetHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"PhoneWindowManager.mBroadcastWakeLock");
mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"PhoneWindowManager.mPowerKeyWakeLock");
mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
mLidKeyboardAccessibility = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lidKeyboardAccessibility);
mLidNavigationAccessibility = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lidNavigationAccessibility);
mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
|| mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
mAllowTheaterModeWakeFromMotion = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
mAllowTheaterModeWakeFromMotionWhenNotDreaming = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming);
mAllowTheaterModeWakeFromCameraLens = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
mAllowTheaterModeWakeFromLidSwitch = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode);
mSupportLongPressPowerWhenNonInteractive = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_supportLongPressPowerWhenNonInteractive);
mSupportShortPressPowerWhenDefaultDisplayOn =
mContext.getResources()
.getBoolean(
com.android.internal.R.bool
.config_supportShortPressPowerWhenDefaultDisplayOn);
mLongPressOnBackBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnBackBehavior);
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
mLongPressOnPowerAssistantTimeoutMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerDurationMs);
mVeryLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_veryLongPressOnPowerBehavior);
mPowerDoublePressTargetActivity = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_doublePressOnPowerTargetActivity));
mPrimaryShortPressTargetActivity = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_primaryShortPressTargetActivity));
mShortPressOnSleepBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnSleepBehavior);
mSilenceRingerOnSleepKey = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_silenceRingerOnSleepKey);
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeKeysInWindowManager);
mWakeUpToLastStateTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_wakeUpToLastStateTimeoutMillis);
mSearchKeyBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_searchKeyBehavior);
mSearchKeyTargetActivity = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_searchKeyTargetActivity));
readConfigurationDependentBehaviors();
mDisplayFoldController = DisplayFoldController.create(mContext, DEFAULT_DISPLAY);
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
// register for dock events
IntentFilter filter = new IntentFilter();
filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE);
filter.addAction(UiModeManager.ACTION_ENTER_DESK_MODE);
filter.addAction(UiModeManager.ACTION_EXIT_DESK_MODE);
filter.addAction(Intent.ACTION_DOCK_EVENT);
Intent intent = mContext.registerReceiver(mDockReceiver, filter);
if (intent != null) {
// Retrieve current sticky dock event broadcast.
mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED));
}
// register for multiuser-relevant broadcasts
filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mMultiuserReceiver, filter);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mHapticFeedbackVibrationProvider =
new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator);
mGlobalKeyManager = new GlobalKeyManager(mContext);
// Controls rotation and the like.
initializeHdmiState();
// Match current screen state.
if (!mPowerManager.isInteractive()) {
startedGoingToSleep(Display.DEFAULT_DISPLAY_GROUP,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
finishedGoingToSleep(Display.DEFAULT_DISPLAY_GROUP,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
}
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@Override
public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
return handleTransitionForKeyguardLw(false /* startKeyguardExitAnimation */,
false /* notifyOccluded */);
}
@Override
public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
// When KEYGUARD_GOING_AWAY app transition is canceled, we need to trigger relevant
// IKeyguardService calls to sync keyguard status in WindowManagerService and SysUI.
handleTransitionForKeyguardLw(
keyguardGoingAwayCancelled /* startKeyguardExitAnimation */,
true /* notifyOccluded */);
synchronized (mLock) {
mLockAfterDreamingTransitionFinished = false;
}
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
synchronized (mLock) {
final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
// check both isDreaming and mLockAfterDreamingTransitionFinished before lockNow
// so it won't relock after dreaming has stopped
if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()
&& mLockAfterDreamingTransitionFinished) {
lockNow(null);
}
mLockAfterDreamingTransitionFinished = false;
}
}
});
mKeyguardDrawnTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_keyguardDrawnTimeout);
mKeyguardDelegate = injector.getKeyguardServiceDelegate();
mTalkbackShortcutController = injector.getTalkbackShortcutController();
initKeyCombinationRules();
initSingleKeyGestureRules(injector.getLooper());
mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager);
}
private void initKeyCombinationRules() {
mKeyCombinationManager = new KeyCombinationManager(mHandler);
final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableScreenshotChord);
if (screenshotChordEnabled) {
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
@Override
void execute() {
mPowerKeyHandled = true;
interceptScreenshotChord(
SCREENSHOT_KEY_CHORD, getScreenshotChordLongPressDelay());
}
@Override
void cancel() {
cancelPendingScreenshotChordAction();
}
});
if (mHasFeatureWatch) {
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_POWER, KEYCODE_STEM_PRIMARY) {
@Override
void execute() {
mPowerKeyHandled = true;
interceptScreenshotChord(SCREENSHOT_KEY_CHORD,
getScreenshotChordLongPressDelay());
}
@Override
void cancel() {
cancelPendingScreenshotChordAction();
}
});
}
}
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP) {
@Override
boolean preCondition() {
return mAccessibilityShortcutController
.isAccessibilityShortcutAvailable(isKeyguardLocked());
}
@Override
void execute() {
interceptAccessibilityShortcutChord();
}
@Override
void cancel() {
cancelPendingAccessibilityShortcutAction();
}
});
// Volume up + power can either be the "ringer toggle chord" or as another way to
// launch GlobalActions. This behavior can change at runtime so we must check behavior
// inside the TwoKeysCombinationRule.
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) {
@Override
boolean preCondition() {
switch (mPowerVolUpBehavior) {
case POWER_VOLUME_UP_BEHAVIOR_MUTE:
return mRingerToggleChord != VOLUME_HUSH_OFF;
default:
return true;
}
}
@Override
void execute() {
switch (mPowerVolUpBehavior) {
case POWER_VOLUME_UP_BEHAVIOR_MUTE:
// no haptic feedback here since
interceptRingerToggleChord();
mPowerKeyHandled = true;
break;
case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
performHapticFeedback(
HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power + Volume Up - Global Actions");
showGlobalActions();
mPowerKeyHandled = true;
break;
default:
break;
}
}
@Override
void cancel() {
switch (mPowerVolUpBehavior) {
case POWER_VOLUME_UP_BEHAVIOR_MUTE:
cancelPendingRingerToggleChordAction();
break;
case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
cancelGlobalActionsAction();
break;
}
}
});
if (mHasFeatureLeanback) {
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
@Override
void execute() {
mBackKeyHandled = true;
interceptAccessibilityGestureTv();
}
@Override
void cancel() {
cancelAccessibilityGestureTv();
}
@Override
long getKeyInterceptDelayMs() {
// Use a timeout of 0 to prevent additional latency in processing of
// this key. This will potentially cause some unwanted UI actions if the
// user does end up triggering the key combination later, but in most
// cases, the user will simply hit a single key, and this will allow us
// to process it without first waiting to see if the combination is
// going to be triggered.
return 0;
}
});
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
@Override
void execute() {
mBackKeyHandled = true;
interceptBugreportGestureTv();
}
@Override
void cancel() {
cancelBugreportGestureTv();
}
@Override
long getKeyInterceptDelayMs() {
return 0;
}
});
}
}
/**
* Rule for single power key gesture.
*/
private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
PowerKeyRule() {
super(KEYCODE_POWER);
}
@Override
boolean supportLongPress() {
return hasLongPressOnPowerBehavior();
}
@Override
boolean supportVeryLongPress() {
return hasVeryLongPressOnPowerBehavior();
}
@Override
int getMaxMultiPressCount() {
return getMaxMultiPressPowerCount();
}
@Override
void onPress(long downTime, int displayId) {
if (mShouldEarlyShortPressOnPower) {
return;
}
powerPress(downTime, 1 /*count*/, displayId);
}
@Override
long getLongPressTimeoutMs() {
if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) {
return mLongPressOnPowerAssistantTimeoutMs;
} else {
return super.getLongPressTimeoutMs();
}
}
@Override
void onLongPress(long eventTime) {
if (mSingleKeyGestureDetector.beganFromNonInteractive()
&& !mSupportLongPressPowerWhenNonInteractive) {
Slog.v(TAG, "Not support long press power when device is not interactive.");
return;
}
powerLongPress(eventTime);
}
@Override
void onVeryLongPress(long eventTime) {
mActivityManagerInternal.prepareForPossibleShutdown();
powerVeryLongPress();
}
@Override
void onMultiPress(long downTime, int count, int displayId) {
powerPress(downTime, count, displayId);
}
@Override
void onKeyUp(long eventTime, int count, int displayId) {
if (mShouldEarlyShortPressOnPower && count == 1) {
powerPress(eventTime, 1 /*pressCount*/, displayId);
}
}
}
/**
* Rule for single back key gesture.
*/
private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
BackKeyRule() {
super(KEYCODE_BACK);
}
@Override
boolean supportLongPress() {
return hasLongPressOnBackBehavior();
}
@Override
int getMaxMultiPressCount() {
return 1;
}
@Override
void onPress(long downTime, int unusedDisplayId) {
mBackKeyHandled |= backKeyPress();
}
@Override
void onLongPress(long downTime) {
backLongPress();
}
}
/**
* Rule for single stem primary key gesture.
*/
private final class StemPrimaryKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
StemPrimaryKeyRule() {
super(KeyEvent.KEYCODE_STEM_PRIMARY);
}
@Override
boolean supportLongPress() {
return hasLongPressOnStemPrimaryBehavior();
}
@Override
int getMaxMultiPressCount() {
return getMaxMultiPressStemPrimaryCount();
}
@Override
void onPress(long downTime, int unusedDisplayId) {
if (mShouldEarlyShortPressOnStemPrimary) {
return;
}
// Short-press should be triggered only if app doesn't handle it.
mDeferredKeyActionExecutor.queueKeyAction(
KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(1 /*count*/));
}
@Override
void onLongPress(long eventTime) {
// Long-press should be triggered only if app doesn't handle it.
mDeferredKeyActionExecutor.queueKeyAction(
KeyEvent.KEYCODE_STEM_PRIMARY,
eventTime,
() -> stemPrimaryLongPress(eventTime));
}
@Override
void onMultiPress(long downTime, int count, int unusedDisplayId) {
// Triple-press stem to toggle accessibility gesture should always be triggered
// regardless of if app handles it.
if (count == 3
&& mTriplePressOnStemPrimaryBehavior
== TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) {
stemPrimaryPress(count);
} else {
// Other multi-press gestures should be triggered only if app doesn't handle it.
mDeferredKeyActionExecutor.queueKeyAction(
KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(count));
}
}
@Override
void onKeyUp(long eventTime, int count, int unusedDisplayId) {
if (count == 1) {
// Save info about the most recent task on the first press of the stem key. This
// may be used later to switch to the most recent app using double press gesture.
// It is possible that we may navigate away from this task before the double
// press is detected, as a result of the first press, so we save the current
// most recent task before that happens.
mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp =
mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
if (mShouldEarlyShortPressOnStemPrimary) {
// Key-up gesture should be triggered only if app doesn't handle it.
mDeferredKeyActionExecutor.queueKeyAction(
KeyEvent.KEYCODE_STEM_PRIMARY, eventTime, () -> stemPrimaryPress(1));
}
}
}
}
private void initSingleKeyGestureRules(Looper looper) {
mSingleKeyGestureDetector = SingleKeyGestureDetector.get(mContext, looper);
mSingleKeyGestureDetector.addRule(new PowerKeyRule());
if (hasLongPressOnBackBehavior()) {
mSingleKeyGestureDetector.addRule(new BackKeyRule());
}
if (hasStemPrimaryBehavior()) {
mSingleKeyGestureDetector.addRule(new StemPrimaryKeyRule());
}
}
/**
* Read values from config.xml that may be overridden depending on
* the configuration of the device.
* eg. Disable long press on home goes to recents on sw600dp.
*/
private void readConfigurationDependentBehaviors() {
final Resources res = mContext.getResources();
mLongPressOnHomeBehavior = res.getInteger(
com.android.internal.R.integer.config_longPressOnHomeBehavior);
if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
mLongPressOnHomeBehavior > LAST_LONG_PRESS_HOME_BEHAVIOR) {
mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
}
mDoubleTapOnHomeBehavior = res.getInteger(
com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_PIP_MENU) {
mDoubleTapOnHomeBehavior = DOUBLE_TAP_HOME_NOTHING;
}
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
mShortPressOnSettingsBehavior = res.getInteger(
com.android.internal.R.integer.config_shortPressOnSettingsBehavior);
if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING
|| mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) {
mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING;
}
}
private void updateSettings() {
updateSettings(null);
}
/**
* Update provider Setting values on a given {@code handler}, or synchronously if {@code null}
* is passed for handler.
*/
void updateSettings(Handler handler) {
if (handler != null) {
handler.post(() -> updateSettings(null));
return;
}
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
synchronized (mLock) {
mEndcallBehavior = Settings.System.getIntForUser(resolver,
Settings.System.END_BUTTON_BEHAVIOR,
Settings.System.END_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
mIncallPowerBehavior = Settings.Secure.getIntForUser(resolver,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
mIncallBackBehavior = Settings.Secure.getIntForUser(resolver,
Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
mSystemNavigationKeysEnabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
0, UserHandle.USER_CURRENT) == 1;
mRingerToggleChord = Settings.Secure.getIntForUser(resolver,
Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
UserHandle.USER_CURRENT);
mPowerButtonSuppressionDelayMillis = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
if (!mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
mRingerToggleChord = VOLUME_HUSH_OFF;
}
// Configure wake gesture.
boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
Settings.Secure.WAKE_GESTURE_ENABLED, 0,
UserHandle.USER_CURRENT) != 0;
if (mWakeGestureEnabledSetting != wakeGestureEnabledSetting) {
mWakeGestureEnabledSetting = wakeGestureEnabledSetting;
updateWakeGestureListenerLp();
}
// use screen off timeout setting as the timeout for the lockscreen
mLockScreenTimeout = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, 0, UserHandle.USER_CURRENT);
String imId = Settings.Secure.getStringForUser(resolver,
Settings.Secure.DEFAULT_INPUT_METHOD, UserHandle.USER_CURRENT);
boolean hasSoftInput = imId != null && imId.length() > 0;
if (mHasSoftInput != hasSoftInput) {
mHasSoftInput = hasSoftInput;
updateRotation = true;
}
mShortPressOnPowerBehavior = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_SHORT_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior));
mDoublePressOnPowerBehavior = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_DOUBLE_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_doublePressOnPowerBehavior));
mTriplePressOnPowerBehavior = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_TRIPLE_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_triplePressOnPowerBehavior));
final int longPressOnPowerBehavior = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_LONG_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior));
final int veryLongPressOnPowerBehavior = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_veryLongPressOnPowerBehavior));
if (mLongPressOnPowerBehavior != longPressOnPowerBehavior
|| mVeryLongPressOnPowerBehavior != veryLongPressOnPowerBehavior) {
mLongPressOnPowerBehavior = longPressOnPowerBehavior;
mVeryLongPressOnPowerBehavior = veryLongPressOnPowerBehavior;
}
mLongPressOnPowerAssistantTimeoutMs = Settings.Global.getLong(
mContext.getContentResolver(),
Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerDurationMs));
mPowerVolUpBehavior = Settings.Global.getInt(resolver,
Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_keyChordPowerVolumeUp));
mShortPressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior));
mDoublePressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer
.config_doublePressOnStemPrimaryBehavior));
mTriplePressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer
.config_triplePressOnStemPrimaryBehavior));
mLongPressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior));
mShouldEarlyShortPressOnPower =
mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_shortPressEarlyOnPower);
mShouldEarlyShortPressOnStemPrimary = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_shortPressEarlyOnStemPrimary);
mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled);
final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
if (mKidsModeEnabled != kidsModeEnabled) {
mKidsModeEnabled = kidsModeEnabled;
updateKidsModeSettings();
}
}
if (updateRotation) {
updateRotation(true);
}
}
private void updateKidsModeSettings() {
if (mKidsModeEnabled) {
// Needed since many Kids apps aren't optimised to support both orientations and it
// will be hard for kids to understand the app compat mode.
// TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once
// possible.
if (mContext.getResources().getBoolean(R.bool.config_reverseDefaultRotation)) {
mWindowManagerInternal.setOrientationRequestPolicy(
true /* isIgnoreOrientationRequestDisabled */,
new int[]{SCREEN_ORIENTATION_LANDSCAPE,
SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
SCREEN_ORIENTATION_SENSOR_LANDSCAPE});
} else {
mWindowManagerInternal.setOrientationRequestPolicy(
true /* isIgnoreOrientationRequestDisabled */,
null /* fromOrientations */, null /* toOrientations */);
}
} else {
mWindowManagerInternal.setOrientationRequestPolicy(
false /* isIgnoreOrientationRequestDisabled */,
null /* fromOrientations */, null /* toOrientations */);
}
}
private DreamManagerInternal getDreamManagerInternal() {
if (mDreamManagerInternal == null) {
// If mDreamManagerInternal is null, attempt to re-fetch it.
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
}
return mDreamManagerInternal;
}
private void updateWakeGestureListenerLp() {
if (shouldEnableWakeGestureLp()) {
mWakeGestureListener.requestWakeUpTrigger();
} else {
mWakeGestureListener.cancelWakeUpTrigger();
}
}
private boolean shouldEnableWakeGestureLp() {
return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake()
&& (getLidBehavior() != LID_BEHAVIOR_SLEEP
|| mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
&& mWakeGestureListener.isSupported();
}
/** {@inheritDoc} */
@Override
public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
int[] outAppOp) {
if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
!= PERMISSION_GRANTED) {
return ADD_PERMISSION_DENIED;
}
outAppOp[0] = AppOpsManager.OP_NONE;
if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)
|| (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
|| (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) {
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
// Window manager will make sure these are okay.
return ADD_OKAY;
}
if (!isSystemAlertWindowType(type)) {
switch (type) {
case TYPE_TOAST:
// Only apps that target older than O SDK can add window without a token, after
// that we require a token so apps cannot add toasts directly as the token is
// added by the notification system.
// Window manager does the checking for this.
outAppOp[0] = OP_TOAST_WINDOW;
return ADD_OKAY;
case TYPE_ACCESSIBILITY_OVERLAY:
if (createAccessibilityOverlayAppOpEnabled()) {
outAppOp[0] = OP_CREATE_ACCESSIBILITY_OVERLAY;
return ADD_OKAY;
}
case TYPE_INPUT_METHOD:
case TYPE_WALLPAPER:
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
case TYPE_VOICE_INTERACTION:
case TYPE_QS_DIALOG:
case TYPE_NAVIGATION_BAR_PANEL:
// The window manager will check these.
return ADD_OKAY;
}
return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
== PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
// Things get a little more interesting for alert windows...
outAppOp[0] = OP_SYSTEM_ALERT_WINDOW;
final int callingUid = Binder.getCallingUid();
// system processes will be automatically granted privilege to draw
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return ADD_OKAY;
}
ApplicationInfo appInfo;
try {
appInfo = mPackageManager.getApplicationInfoAsUser(
packageName,
0 /* flags */,
UserHandle.getUserId(callingUid));
} catch (PackageManager.NameNotFoundException e) {
appInfo = null;
}
if (appInfo == null || (type != TYPE_APPLICATION_OVERLAY && appInfo.targetSdkVersion >= O)) {
/**
* Apps targeting >= {@link Build.VERSION_CODES#O} are required to hold
* {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} (system signature apps)
* permission to add alert windows that aren't
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}.
*/
return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
== PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
if (mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
== PERMISSION_GRANTED) {
return ADD_OKAY;
}
// check if user has enabled this operation. SecurityException will be thrown if this app
// has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
// make sure the usage is logged.
final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName,
null /* featureId */, "check-add");
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_IGNORED:
// although we return ADD_OKAY for MODE_IGNORED, the added window will
// actually be hidden in WindowManagerService
return ADD_OKAY;
case AppOpsManager.MODE_ERRORED:
// Don't crash legacy apps
if (appInfo.targetSdkVersion < M) {
return ADD_OKAY;
}
return ADD_PERMISSION_DENIED;
default:
// in the default mode, we will make a decision here based on
// checkCallingPermission()
return (mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
== PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
}
void readLidState() {
mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
}
private void readCameraLensCoverState() {
mCameraLensCoverState = mWindowManagerFuncs.getCameraLensCoverState();
}
private boolean isHidden(int accessibilityMode) {
final int lidState = mDefaultDisplayPolicy.getLidState();
switch (accessibilityMode) {
case 1:
return lidState == LID_CLOSED;
case 2:
return lidState == LID_OPEN;
default:
return false;
}
}
/** {@inheritDoc} */
@Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;
readConfigurationDependentBehaviors();
readLidState();
if (config.keyboard == Configuration.KEYBOARD_NOKEYS
|| (keyboardPresence == PRESENCE_INTERNAL
&& isHidden(mLidKeyboardAccessibility))) {
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
if (!mHasSoftInput) {
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_YES;
}
}
if (config.navigation == Configuration.NAVIGATION_NONAV
|| (navigationPresence == PRESENCE_INTERNAL
&& isHidden(mLidNavigationAccessibility))) {
config.navigationHidden = Configuration.NAVIGATIONHIDDEN_YES;
}
}
@Override
public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
@Override
public Animation createHiddenByKeyguardExit(boolean onWallpaper,
boolean goingToNotificationShade, boolean subtleAnimation) {
return TransitionAnimation.createHiddenByKeyguardExit(mContext,
mLogDecelerateInterpolator, onWallpaper, goingToNotificationShade, subtleAnimation);
}
@Override
public Animation createKeyguardWallpaperExit(boolean goingToNotificationShade) {
if (goingToNotificationShade) {
return null;
} else {
return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit);
}
}
private static void awakenDreams() {
IDreamManager dreamManager = getDreamManager();
if (dreamManager != null) {
try {
dreamManager.awaken();
} catch (RemoteException e) {
// fine, stay asleep then
}
}
}
static IDreamManager getDreamManager() {
return IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
}
TelecomManager getTelecommService() {
return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
}
NotificationManager getNotificationService() {
return mContext.getSystemService(NotificationManager.class);
}
static IAudioService getAudioService() {
IAudioService audioService = IAudioService.Stub.asInterface(
ServiceManager.checkService(Context.AUDIO_SERVICE));
if (audioService == null) {
Log.w(TAG, "Unable to find IAudioService interface.");
}
return audioService;
}
boolean keyguardOn() {
return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
}
private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
};
/**
* Log the keyboard shortcuts without blocking the current thread.
*
* We won't log keyboard events when the input device is null
* or when it is virtual.
*/
private void handleKeyboardSystemEvent(KeyboardLogEvent keyboardLogEvent, KeyEvent event) {
final InputDevice inputDevice = mInputManager.getInputDevice(event.getDeviceId());
KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice,
keyboardLogEvent, event.getMetaState(), event.getKeyCode());
event.recycle();
}
private void logKeyboardSystemsEventOnActionUp(KeyEvent event,
KeyboardLogEvent keyboardSystemEvent) {
if (event.getAction() != KeyEvent.ACTION_UP) {
return;
}
logKeyboardSystemsEvent(event, keyboardSystemEvent);
}
private void logKeyboardSystemsEventOnActionDown(KeyEvent event,
KeyboardLogEvent keyboardSystemEvent) {
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return;
}
logKeyboardSystemsEvent(event, keyboardSystemEvent);
}
private void logKeyboardSystemsEvent(KeyEvent event, KeyboardLogEvent keyboardSystemEvent) {
KeyEvent eventToLog = KeyEvent.obtain(event);
mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, keyboardSystemEvent.getIntValue(), 0,
eventToLog).sendToTarget();
}
// TODO(b/117479243): handle it in InputPolicy
// TODO (b/283241997): Add the remaining keyboard shortcut logging after refactoring
/** {@inheritDoc} */
@Override
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
int policyFlags) {
final int keyCode = event.getKeyCode();
final int flags = event.getFlags();
final long keyConsumed = -1;
final long keyNotConsumed = 0;
final int deviceId = event.getDeviceId();
if (DEBUG_INPUT) {
Log.d(TAG,
"interceptKeyTi keyCode=" + keyCode + " action=" + event.getAction()
+ " repeatCount=" + event.getRepeatCount() + " keyguardOn="
+ keyguardOn() + " canceled=" + event.isCanceled());
}
if (mKeyCombinationManager.isKeyConsumed(event)) {
return keyConsumed;
}
if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
final long now = SystemClock.uptimeMillis();
final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode);
if (now < interceptTimeout) {
return interceptTimeout - now;
}
}
Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
if (consumedKeys == null) {
consumedKeys = new HashSet<>();
mConsumedKeysForDevice.put(deviceId, consumedKeys);
}
if (interceptSystemKeysAndShortcuts(focusedToken, event)
&& event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
consumedKeys.add(keyCode);
return keyConsumed;
}
boolean needToConsumeKey = consumedKeys.contains(keyCode);
if (event.getAction() == KeyEvent.ACTION_UP || event.isCanceled()) {
consumedKeys.remove(keyCode);
if (consumedKeys.isEmpty()) {
mConsumedKeysForDevice.remove(deviceId);
}
}
return needToConsumeKey ? keyConsumed : keyNotConsumed;
}
// You can only start consuming the key gesture if ACTION_DOWN and repeat count
// is 0. If you start intercepting the key halfway, then key will not be consumed
// and will be sent to apps for processing too.
// e.g. If a certain combination is only handled on ACTION_UP (i.e.
// interceptShortcut() returns true only for ACTION_UP), then since we already
// sent the ACTION_DOWN events to the application, we MUST also send the
// ACTION_UP to the application.
// So, to ensure that your intercept logic works properly, and we don't send any
// conflicting events to application, make sure to consume the event on
// ACTION_DOWN even if you want to do something on ACTION_UP. This is essential
// to maintain event parity and to not have incomplete key gestures.
@SuppressLint("MissingPermission")
private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final int metaState = event.getMetaState();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int displayId = event.getDisplayId();
final int deviceId = event.getDeviceId();
final boolean firstDown = down && repeatCount == 0;
// Cancel any pending meta actions if we see any other keys being pressed between the
// down of the meta key and its corresponding up.
if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
mPendingMetaAction = false;
}
// Any key that is not Alt or Meta cancels Caps Lock combo tracking.
if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
mPendingCapsLockToggle = false;
}
if (isUserSetupComplete() && !keyguardOn) {
if (mModifierShortcutManager.interceptKey(event)) {
dismissKeyboardShortcutsMenu();
mPendingMetaAction = false;
mPendingCapsLockToggle = false;
return true;
}
}
switch (keyCode) {
case KeyEvent.KEYCODE_HOME:
return handleHomeShortcuts(focusedToken, event);
case KeyEvent.KEYCODE_MENU:
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
if (mEnableShiftMenuBugReports && firstDown
&& (metaState & chordBug) == chordBug) {
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
null, null, null, 0, null, null);
logKeyboardSystemsEvent(event, KeyboardLogEvent.TRIGGER_BUG_REPORT);
return true;
}
break;
case KeyEvent.KEYCODE_RECENT_APPS:
if (firstDown) {
showRecentApps(false /* triggeredFromAltTab */);
logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
}
return true;
case KeyEvent.KEYCODE_APP_SWITCH:
if (!keyguardOn) {
if (firstDown) {
preloadRecentApps();
} else if (!down) {
toggleRecentApps();
logKeyboardSystemsEvent(event, KeyboardLogEvent.APP_SWITCH);
}
}
return true;
case KeyEvent.KEYCODE_A:
if (firstDown && event.isMetaPressed()) {
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
deviceId, event.getEventTime(),
AssistUtils.INVOCATION_TYPE_UNKNOWN);
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
return true;
}
break;
case KeyEvent.KEYCODE_H:
case KeyEvent.KEYCODE_ENTER:
if (event.isMetaPressed()) {
return handleHomeShortcuts(focusedToken, event);
}
break;
case KeyEvent.KEYCODE_I:
if (firstDown && event.isMetaPressed()) {
showSystemSettings();
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS);
return true;
}
break;
case KeyEvent.KEYCODE_L:
if (firstDown && event.isMetaPressed()) {
lockNow(null /* options */);
logKeyboardSystemsEvent(event, KeyboardLogEvent.LOCK_SCREEN);
return true;
}
break;
case KeyEvent.KEYCODE_N:
if (firstDown && event.isMetaPressed()) {
if (event.isCtrlPressed()) {
sendSystemKeyToStatusBarAsync(event);
logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_NOTES);
} else {
toggleNotificationPanel();
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
}
return true;
}
break;
case KeyEvent.KEYCODE_S:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
logKeyboardSystemsEvent(event, KeyboardLogEvent.TAKE_SCREENSHOT);
return true;
}
break;
case KeyEvent.KEYCODE_T:
if (firstDown && event.isMetaPressed()) {
toggleTaskbar();
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_TASKBAR);
return true;
}
break;
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_ESCAPE:
if (firstDown && event.isMetaPressed()) {
logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
injectBackGesture(event.getDownTime());
return true;
}
case KeyEvent.KEYCODE_DPAD_UP:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.goToFullscreenFromSplit();
logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
return true;
}
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
if (firstDown && event.isMetaPressed()) {
if (event.isCtrlPressed()) {
enterStageSplitFromRunningApp(true /* leftOrTop */);
logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
} else {
logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
injectBackGesture(event.getDownTime());
}
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
enterStageSplitFromRunningApp(false /* leftOrTop */);
logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
return true;
}
break;
case KeyEvent.KEYCODE_SLASH:
if (firstDown && event.isMetaPressed() && !keyguardOn) {
toggleKeyboardShortcutsMenu(event.getDeviceId());
logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_SHORTCUT_HELPER);
return true;
}
break;
case KeyEvent.KEYCODE_ASSIST:
Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
return true;
case KeyEvent.KEYCODE_VOICE_ASSIST:
Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
+ " interceptKeyBeforeQueueing");
return true;
case KeyEvent.KEYCODE_VIDEO_APP_1:
case KeyEvent.KEYCODE_VIDEO_APP_2:
case KeyEvent.KEYCODE_VIDEO_APP_3:
case KeyEvent.KEYCODE_VIDEO_APP_4:
case KeyEvent.KEYCODE_VIDEO_APP_5:
case KeyEvent.KEYCODE_VIDEO_APP_6:
case KeyEvent.KEYCODE_VIDEO_APP_7:
case KeyEvent.KEYCODE_VIDEO_APP_8:
case KeyEvent.KEYCODE_FEATURED_APP_1:
case KeyEvent.KEYCODE_FEATURED_APP_2:
case KeyEvent.KEYCODE_FEATURED_APP_3:
case KeyEvent.KEYCODE_FEATURED_APP_4:
case KeyEvent.KEYCODE_DEMO_APP_1:
case KeyEvent.KEYCODE_DEMO_APP_2:
case KeyEvent.KEYCODE_DEMO_APP_3:
case KeyEvent.KEYCODE_DEMO_APP_4:
Slog.wtf(TAG, "KEYCODE_APP_X should be handled in interceptKeyBeforeQueueing");
return true;
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
if (down) {
int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
// Disable autobrightness if it's on
int auto = Settings.System.getIntForUser(
mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
UserHandle.USER_CURRENT_OR_SELF);
if (auto != 0) {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
UserHandle.USER_CURRENT_OR_SELF);
}
int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
float minLinearBrightness = mPowerManager.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);
float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
float adjustedGammaBrightness =
gammaBrightness + 1f / BRIGHTNESS_STEPS * direction;
adjustedGammaBrightness = MathUtils.constrain(adjustedGammaBrightness, 0f,
1f);
float adjustedLinearBrightness = BrightnessUtils.convertGammaToLinear(
adjustedGammaBrightness);
adjustedLinearBrightness = MathUtils.constrain(adjustedLinearBrightness,
minLinearBrightness, maxLinearBrightness);
mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
| Intent.FLAG_ACTIVITY_NO_USER_ACTION);
intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
logKeyboardSystemsEvent(event, KeyboardLogEvent.getBrightnessEvent(keyCode));
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
if (down) {
mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId());
logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
if (down) {
mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId());
logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
// TODO: Add logic
if (!down) {
logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE);
}
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
if (mUseTvRouting || mHandleVolumeKeysInWM) {
// On TVs or when the configuration is enabled, volume keys never
// go to the foreground app.
dispatchDirectAudioEvent(event);
return true;
}
// If the device is in VR mode and keys are "internal" (e.g. on the side of the
// device), then drop the volume keys and don't forward it to the
// application/dispatch the audio event.
if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
final InputDevice d = event.getDevice();
if (d != null && !d.isExternal()) {
return true;
}
}
break;
case KeyEvent.KEYCODE_TAB:
if (firstDown && !keyguardOn && isUserSetupComplete()) {
if (event.isMetaPressed()) {
showRecentApps(false);
logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
return true;
} else if (mRecentAppsHeldModifiers == 0) {
final int shiftlessModifiers =
event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
if (KeyEvent.metaStateHasModifiers(
shiftlessModifiers, KeyEvent.META_ALT_ON)) {
mRecentAppsHeldModifiers = shiftlessModifiers;
showRecentApps(true);
logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
return true;
}
}
}
break;
case KeyEvent.KEYCODE_ALL_APPS:
if (!down) {
mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
msg.setAsynchronous(true);
msg.sendToTarget();
logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
if (!down) {
toggleNotificationPanel();
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
}
return true;
case KeyEvent.KEYCODE_SEARCH:
if (firstDown && !keyguardOn) {
switch (mSearchKeyBehavior) {
case SEARCH_BEHAVIOR_TARGET_ACTIVITY: {
launchTargetSearchActivity();
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SEARCH);
return true;
}
case SEARCH_BEHAVIOR_DEFAULT_SEARCH:
default:
break;
}
}
break;
case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
if (firstDown) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
sendSwitchKeyboardLayout(event, focusedToken, direction);
logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
return true;
}
break;
case KeyEvent.KEYCODE_META_LEFT:
case KeyEvent.KEYCODE_META_RIGHT:
if (down) {
if (event.isAltPressed()) {
mPendingCapsLockToggle = true;
mPendingMetaAction = false;
} else {
mPendingCapsLockToggle = false;
mPendingMetaAction = true;
}
} else {
// Toggle Caps Lock on META-ALT.
if (mPendingCapsLockToggle) {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
} else if (mPendingMetaAction) {
if (!canceled) {
launchAllAppsViaA11y();
logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
}
mPendingMetaAction = false;
}
}
return true;
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
if (down) {
if (event.isMetaPressed()) {
mPendingCapsLockToggle = true;
mPendingMetaAction = false;
} else {
mPendingCapsLockToggle = false;
}
} else {
// hide recent if triggered by ALT-TAB.
if (mRecentAppsHeldModifiers != 0
&& (metaState & mRecentAppsHeldModifiers) == 0) {
mRecentAppsHeldModifiers = 0;
hideRecentApps(true, false);
return true;
}
// Toggle Caps Lock on META-ALT.
if (mPendingCapsLockToggle) {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
return true;
}
}
break;
case KeyEvent.KEYCODE_CAPS_LOCK:
if (!down) {
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
}
break;
case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
+ " interceptKeyBeforeQueueing");
return true;
case KeyEvent.KEYCODE_SETTINGS:
if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) {
if (!down) {
toggleNotificationPanel();
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
}
return true;
}
break;
case KeyEvent.KEYCODE_STEM_PRIMARY:
if (prepareToSendSystemKeyToApplication(focusedToken, event)) {
// Send to app.
return false;
} else {
// Intercepted.
sendSystemKeyToStatusBarAsync(event);
return true;
}
}
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
return true;
}
// Reserve all the META modifier combos for system behavior
return (metaState & KeyEvent.META_META_ON) != 0;
}
/**
* In this function, we check whether a system key should be sent to the application. We also
* detect the key gesture on this key, even if the key will be sent to the app. The gesture
* action, if any, will not be executed immediately. It will be queued and execute only after
* the application tells us that it didn't handle this key.
*
* @return true if this key should be sent to the application. This also means that the target
* application has the necessary permissions to receive this key. Return false otherwise.
*/
private boolean prepareToSendSystemKeyToApplication(IBinder focusedToken, KeyEvent event) {
final int keyCode = event.getKeyCode();
if (!event.isSystem()) {
Log.wtf(
TAG,
"Illegal keycode provided to prepareToSendSystemKeyToApplication: "
+ KeyEvent.keyCodeToString(keyCode));
return false;
}
final boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
if (isDown && event.getRepeatCount() == 0) {
// This happens at the initial DOWN event. Check focused window permission now.
final KeyInterceptionInfo info =
mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
if (info != null
&& mButtonOverridePermissionChecker.canAppOverrideSystemKey(
mContext, info.windowOwnerUid)) {
// Focused window has the permission. Pass the event to it.
return true;
} else {
// Focused window doesn't have the permission. Intercept the event.
// If the initial DOWN event is intercepted, follow-up events will be intercepted
// too. So we know the gesture won't be handled by app, and can handle the gesture
// in system.
setDeferredKeyActionsExecutableAsync(keyCode, event.getDownTime());
return false;
}
} else {
// This happens after the initial DOWN event. We will just reuse the initial decision.
// I.e., if the initial DOWN event was dispatched, follow-up events should be
// dispatched. Otherwise, follow-up events should be consumed.
final Set<Integer> consumedKeys = mConsumedKeysForDevice.get(event.getDeviceId());
final boolean wasConsumed = consumedKeys != null && consumedKeys.contains(keyCode);
return !wasConsumed;
}
}
private void setDeferredKeyActionsExecutableAsync(int keyCode, long downTime) {
Message msg = Message.obtain(mHandler, MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE);
msg.arg1 = keyCode;
msg.obj = downTime;
msg.setAsynchronous(true);
msg.sendToTarget();
}
@SuppressLint("MissingPermission")
private void injectBackGesture(long downtime) {
// Create and inject down event
KeyEvent downEvent = new KeyEvent(downtime, downtime, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
mInputManager.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
// Create and inject up event
KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP);
mInputManager.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
downEvent.recycle();
upEvent.recycle();
}
private boolean handleHomeShortcuts(IBinder focusedToken, KeyEvent event) {
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
// timeout.
DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(event.getDisplayId());
if (handler == null) {
handler = new DisplayHomeButtonHandler(event.getDisplayId());
mDisplayHomeButtonHandlers.put(event.getDisplayId(), handler);
}
return handler.handleHomeButton(focusedToken, event);
}
private void toggleMicrophoneMuteFromKey() {
if (mSensorPrivacyManager.supportsSensorToggle(
SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
SensorPrivacyManager.Sensors.MICROPHONE)) {
boolean isEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(
SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
SensorPrivacyManager.Sensors.MICROPHONE);
mSensorPrivacyManager.setSensorPrivacy(SensorPrivacyManager.Sensors.MICROPHONE,
!isEnabled);
int toastTextResId;
if (isEnabled) {
toastTextResId = R.string.mic_access_on_toast;
} else {
toastTextResId = R.string.mic_access_off_toast;
}
Toast.makeText(
mContext,
UiThread.get().getLooper(),
mContext.getString(toastTextResId),
Toast.LENGTH_SHORT)
.show();
}
}
/**
* TV only: recognizes a remote control gesture for capturing a bug report.
*/
private void interceptBugreportGestureTv() {
mHandler.removeMessages(MSG_BUGREPORT_TV);
// The bugreport capture chord is a long press on DPAD CENTER and BACK simultaneously.
Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS);
}
private void cancelBugreportGestureTv() {
mHandler.removeMessages(MSG_BUGREPORT_TV);
}
/**
* TV only: recognizes a remote control gesture as Accessibility shortcut.
* Shortcut: Long press (BACK + DPAD_DOWN)
*/
private void interceptAccessibilityGestureTv() {
mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout());
}
private void cancelAccessibilityGestureTv() {
mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
}
private void requestBugreportForTv() {
try {
if (!ActivityManager.getService().launchBugReportHandlerApp()) {
ActivityManager.getService().requestInteractiveBugReport();
}
} catch (RemoteException e) {
Slog.e(TAG, "Error taking bugreport", e);
}
}
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
// Note: This method is only called if the initial down was unhandled.
if (DEBUG_INPUT) {
final KeyInterceptionInfo info =
mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
final String title = info == null ? "<unknown>" : info.windowTitle;
Slog.d(TAG, "Unhandled key: inputToken=" + focusedToken
+ ", title=" + title
+ ", action=" + event.getAction()
+ ", flags=" + event.getFlags()
+ ", keyCode=" + event.getKeyCode()
+ ", scanCode=" + event.getScanCode()
+ ", metaState=" + event.getMetaState()
+ ", repeatCount=" + event.getRepeatCount()
+ ", policyFlags=" + policyFlags);
}
if (interceptUnhandledKey(event, focusedToken)) {
return null;
}
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}
if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), event.getDisplayId(), null);
if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
if (DEBUG_INPUT) {
if (fallbackEvent == null) {
Slog.d(TAG, "No fallback.");
} else {
Slog.d(TAG, "Performing fallback: " + fallbackEvent);
}
}
return fallbackEvent;
}
private boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final int metaState = event.getModifiers();
switch(keyCode) {
case KeyEvent.KEYCODE_SPACE:
if (down && repeatCount == 0) {
// Handle keyboard layout switching. (CTRL + SPACE)
if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
KeyEvent.META_CTRL_ON)) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
sendSwitchKeyboardLayout(event, focusedToken, direction);
return true;
}
}
break;
case KeyEvent.KEYCODE_Z:
if (down && KeyEvent.metaStateHasModifiers(metaState,
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) {
// Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
if (mAccessibilityShortcutController
.isAccessibilityShortcutAvailable(isKeyguardLocked())) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
return true;
}
}
break;
case KeyEvent.KEYCODE_SYSRQ:
if (down && repeatCount == 0) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
}
return true;
case KeyEvent.KEYCODE_ESCAPE:
if (down
&& KeyEvent.metaStateHasNoModifiers(metaState)
&& repeatCount == 0) {
mContext.closeSystemDialogs();
}
return true;
case KeyEvent.KEYCODE_STEM_PRIMARY:
handleUnhandledSystemKey(event);
sendSystemKeyToStatusBarAsync(event);
return true;
}
return false;
}
/**
* Called when a system key was sent to application and was unhandled. We will execute any
* queued actions associated with this key code at this point.
*/
private void handleUnhandledSystemKey(KeyEvent event) {
if (!event.isSystem()) {
Log.wtf(
TAG,
"Illegal keycode provided to handleUnhandledSystemKey: "
+ KeyEvent.keyCodeToString(event.getKeyCode()));
return;
}
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
// If the initial DOWN event is unhandled by app, follow-up events will also be
// unhandled by app. So we can handle the key event in system.
setDeferredKeyActionsExecutableAsync(event.getKeyCode(), event.getDownTime());
}
}
private void sendSwitchKeyboardLayout(@NonNull KeyEvent event,
@Nullable IBinder focusedToken, int direction) {
SwitchKeyboardLayoutMessageObject object =
new SwitchKeyboardLayoutMessageObject(event, focusedToken, direction);
mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget();
}
private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction,
IBinder focusedToken) {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
IBinder targetWindowToken =
mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction,
event.getDisplayId(), targetWindowToken);
} else {
mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
}
}
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
focusedToken, fallbackEvent, policyFlags);
if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken)) {
return true;
}
}
return false;
}
@Override
public void setTopFocusedDisplay(int displayId) {
mTopFocusedDisplayId = displayId;
}
@Override
public void registerDisplayFoldListener(IDisplayFoldListener listener) {
if (mDisplayFoldController != null) {
mDisplayFoldController.registerDisplayFoldListener(listener);
}
}
@Override
public void unregisterDisplayFoldListener(IDisplayFoldListener listener) {
if (mDisplayFoldController != null) {
mDisplayFoldController.unregisterDisplayFoldListener(listener);
}
}
@Override
public void setOverrideFoldedArea(Rect area) {
if (mDisplayFoldController != null) {
mDisplayFoldController.setOverrideFoldedArea(area);
}
}
@Override
public Rect getFoldedArea() {
if (mDisplayFoldController != null) {
return mDisplayFoldController.getFoldedArea();
}
return new Rect();
}
@Override
public void onDefaultDisplayFocusChangedLw(WindowState newFocus) {
if (mDisplayFoldController != null) {
mDisplayFoldController.onDefaultDisplayFocusChanged(
newFocus != null ? newFocus.getOwningPackage() : null);
}
}
@Override
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
throws RemoteException {
synchronized (mLock) {
mModifierShortcutManager.registerShortcutKey(shortcutCode, shortcutService);
}
}
@Override
public void onKeyguardOccludedChangedLw(boolean occluded) {
if (mKeyguardDelegate != null) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
}
}
@Override
public int applyKeyguardOcclusionChange() {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
+ mPendingKeyguardOccluded + " changed=" + mKeyguardOccludedChanged);
// TODO(b/276433230): Explicitly save before/after for occlude state in each
// Transition so we don't need to update SysUI every time.
if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
} else {
return 0;
}
}
/**
* Called when keyguard related app transition starts, or cancelled.
*
* @param startKeyguardExitAnimation Trigger IKeyguardService#startKeyguardExitAnimation to
* start keyguard exit animation.
* @param notifyOccluded Trigger IKeyguardService#setOccluded binder call to notify whether
* the top activity can occlude the keyguard or not.
*
* @return Whether the flags have changed and we have to redo the layout.
*/
private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
boolean notifyOccluded) {
int redoLayout = 0;
if (notifyOccluded) {
redoLayout = applyKeyguardOcclusionChange();
}
if (startKeyguardExitAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis());
}
return redoLayout;
}
// There are several different flavors of "assistant" that can be launched from
// various parts of the UI.
/** Asks the status bar to startAssist(), usually a full "assistant" interface */
private void launchAssistAction(String hint, int deviceId, long eventTime,
int invocationType) {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
if (!isUserSetupComplete()) {
// Disable opening assist window during setup
return;
}
// Add Intent Extra data.
Bundle args = null;
args = new Bundle();
if (deviceId != INVALID_INPUT_DEVICE_ID) {
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId);
}
if (hint != null) {
args.putBoolean(hint, true);
}
args.putLong(Intent.EXTRA_TIME, eventTime);
args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
SearchManager searchManager = mContext.getSystemService(SearchManager.class);
if (searchManager != null) {
searchManager.launchAssist(args);
} else {
// Fallback to status bar if search manager doesn't exist (e.g. on wear).
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
statusBar.startAssist(args);
}
}
}
/**
* Launches ACTION_VOICE_ASSIST_RETAIL if in retail mode, or ACTION_VOICE_ASSIST otherwise
* Does nothing on keyguard except for watches. Delegates it to keyguard if present on watch.
*/
private void launchVoiceAssist(boolean allowDuringSetup) {
final boolean keyguardActive =
mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (!keyguardActive) {
startActivityAsUser(
new Intent(Intent.ACTION_VOICE_ASSIST),
/* bundle= */ null,
UserHandle.CURRENT_OR_SELF,
allowDuringSetup);
} else {
mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
}
}
private boolean isInRetailMode() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
}
private void startActivityAsUser(Intent intent, UserHandle handle) {
startActivityAsUser(intent, null, handle);
}
private void startActivityAsUser(Intent intent, Bundle bundle, UserHandle handle) {
startActivityAsUser(intent, bundle, handle, false /* allowDuringSetup */);
}
private void startActivityAsUser(Intent intent, Bundle bundle, UserHandle handle,
boolean allowDuringSetup) {
if (allowDuringSetup || isUserSetupComplete()) {
mContext.startActivityAsUser(intent, bundle, handle);
} else {
Slog.i(TAG, "Not starting activity because user setup is in progress: " + intent);
}
}
private void preloadRecentApps() {
mPreloadedRecentApps = true;
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.preloadRecentApps();
}
}
private void cancelPreloadRecentApps() {
if (mPreloadedRecentApps) {
mPreloadedRecentApps = false;
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.cancelPreloadRecentApps();
}
}
}
private void toggleTaskbar() {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.toggleTaskbar();
}
}
private void toggleRecentApps() {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.toggleRecentApps();
}
}
@Override
public void showRecentApps() {
mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS).sendToTarget();
}
private void showRecentApps(boolean triggeredFromAltTab) {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.showRecentApps(triggeredFromAltTab);
}
}
private void toggleKeyboardShortcutsMenu(int deviceId) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.toggleKeyboardShortcutsMenu(deviceId);
}
}
private void dismissKeyboardShortcutsMenu() {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.dismissKeyboardShortcutsMenu();
}
}
private void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHome) {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.hideRecentApps(triggeredFromAltTab, triggeredFromHome);
}
}
private void enterStageSplitFromRunningApp(boolean leftOrTop) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.enterStageSplitFromRunningApp(leftOrTop);
}
}
void launchHomeFromHotKey(int displayId) {
launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
}
/**
* A home key -> launch home action was detected. Take the appropriate action
* given the situation with the keyguard.
*/
void launchHomeFromHotKey(int displayId, final boolean awakenFromDreams,
final boolean respectKeyguard) {
if (respectKeyguard) {
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
return;
}
if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@Override
public void onKeyguardExitResult(boolean success) {
if (success) {
final long origId = Binder.clearCallingIdentity();
try {
startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
});
return;
}
}
// no keyguard stuff to worry about, just launch home!
if (mRecentsVisible) {
try {
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
// Hide Recents and notify it to launch Home
if (awakenFromDreams) {
awakenDreams();
}
hideRecentApps(false, true);
} else {
// Otherwise, just launch Home
startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
}
}
@Override
public void setRecentsVisibilityLw(boolean visible) {
mRecentsVisible = visible;
}
@Override
public void setPipVisibilityLw(boolean visible) {
mPictureInPictureVisible = visible;
}
@Override
public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) {
mNavBarVirtualKeyHapticFeedbackEnabled = enabled;
}
/**
* Updates the occluded state of the Keyguard immediately via
* {@link com.android.internal.policy.IKeyguardService}.
*
* @param isOccluded Whether the Keyguard is occluded by another window.
* @return Whether the flags have changed and we have to redo the layout.
*/
private boolean setKeyguardOccludedLw(boolean isOccluded) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
mPendingKeyguardOccluded = isOccluded;
mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
return mKeyguardDelegate.isShowing();
}
/** {@inheritDoc} */
@Override
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
// lid changed state
final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED;
if (newLidState == mDefaultDisplayPolicy.getLidState()) {
return;
}
mDefaultDisplayPolicy.setLidState(newLidState);
applyLidSwitchState();
updateRotation(true);
if (lidOpen) {
wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
PowerManager.WAKE_REASON_LID, "android.policy:LID");
} else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
@Override
public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
int lensCoverState = lensCovered ? CAMERA_LENS_COVERED : CAMERA_LENS_UNCOVERED;
if (mCameraLensCoverState == lensCoverState) {
return;
}
if (!mContext.getResources().getBoolean(
R.bool.config_launchCameraOnCameraLensCoverToggle)) {
return;
}
if (mCameraLensCoverState == CAMERA_LENS_COVERED &&
lensCoverState == CAMERA_LENS_UNCOVERED) {
Intent intent;
final boolean keyguardActive = mKeyguardDelegate == null ? false :
mKeyguardDelegate.isShowing();
if (keyguardActive) {
intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
} else {
intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
}
wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens,
PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER");
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
mCameraLensCoverState = lensCoverState;
}
void initializeHdmiState() {
final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
initializeHdmiStateInternal();
} finally {
StrictMode.setThreadPolicyMask(oldMask);
}
}
void initializeHdmiStateInternal() {
boolean plugged = false;
// watch for HDMI plug messages if the hdmi switch exists
if (new File("/sys/devices/virtual/switch/hdmi/state").exists()) {
mHDMIObserver.startObserving("DEVPATH=/devices/virtual/switch/hdmi");
final String filename = "/sys/class/switch/hdmi/state";
FileReader reader = null;
try {
reader = new FileReader(filename);
char[] buf = new char[15];
int n = reader.read(buf);
if (n > 1) {
plugged = 0 != Integer.parseInt(new String(buf, 0, n - 1));
}
} catch (IOException ex) {
Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
} catch (NumberFormatException ex) {
Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
}
}
}
} else {
final List<ExtconUEventObserver.ExtconInfo> extcons =
ExtconUEventObserver.ExtconInfo.getExtconInfoForTypes(
new String[] {ExtconUEventObserver.ExtconInfo.EXTCON_HDMI});
if (!extcons.isEmpty()) {
// TODO: handle more than one HDMI
HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
plugged = observer.init(extcons.get(0));
mHDMIObserver = observer;
} else if (localLOGV) {
Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
}
}
// This dance forces the code in setHdmiPlugged to run.
// Always do this so the sticky intent is stuck (to false) if there is no hdmi.
mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
}
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
final int keyCode = event.getKeyCode();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
|| event.isWakeKey();
if (!mSystemBooted) {
// If we have not yet booted, don't let key events do anything.
// Exception: Wake and power key events are forwarded to PowerManager to allow it to
// wake from quiescent mode during boot. On these key events we also explicitly turn on
// the connected TV and switch HDMI input if we're a HDMI playback device.
boolean shouldTurnOnTv = false;
if (down && (keyCode == KeyEvent.KEYCODE_POWER
|| keyCode == KeyEvent.KEYCODE_TV_POWER)) {
wakeUpFromPowerKey(event.getDownTime());
shouldTurnOnTv = true;
} else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
&& isWakeKeyWhenScreenOff(keyCode)) {
wakeUpFromWakeKey(event);
shouldTurnOnTv = true;
}
if (shouldTurnOnTv) {
final HdmiControl hdmiControl = getHdmiControl();
if (hdmiControl != null) {
hdmiControl.turnOnTv();
}
}
return 0;
}
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
final boolean canceled = event.isCanceled();
final int displayId = event.getDisplayId();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
if (DEBUG_INPUT) {
// If screen is off then we treat the case where the keyguard is open but hidden
// the same as if it were open and in front.
// This will prevent any keys other than the power button from waking the screen
// when the keyguard is hidden by another activity.
final boolean keyguardActive = (mKeyguardDelegate != null
&& (interactive ? isKeyguardShowingAndNotOccluded() :
mKeyguardDelegate.isShowing()));
Log.d(TAG, "interceptKeyTq keycode=" + keyCode
+ " interactive=" + interactive + " keyguardActive=" + keyguardActive
+ " policyFlags=" + Integer.toHexString(policyFlags));
}
// Basic policy based on interactive state.
int result;
if (interactive || (isInjected && !isWakeKey)) {
// When the device is interactive or the key is injected pass the
// key to the application.
result = ACTION_PASS_TO_USER;
isWakeKey = false;
if (interactive) {
// If the screen is awake, but the button pressed was the one that woke the device
// then don't pass it to the application
if (keyCode == mPendingWakeKey && !down) {
result = 0;
}
// Reset the pending key
mPendingWakeKey = PENDING_KEY_NULL;
}
} else if (shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
// If we're currently dozing with the screen on and the keyguard showing, pass the key
// to the application but preserve its wake key status to make sure we still move
// from dozing to fully interactive if we would normally go from off to fully
// interactive.
result = ACTION_PASS_TO_USER;
// Since we're dispatching the input, reset the pending key
mPendingWakeKey = PENDING_KEY_NULL;
} else {
// When the screen is off and the key is not injected, determine whether
// to wake the device but don't pass the key to the application.
result = 0;
if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
isWakeKey = false;
}
// Cache the wake key on down event so we can also avoid sending the up event to the app
if (isWakeKey && down) {
mPendingWakeKey = keyCode;
}
}
// If the key would be handled globally, just return the result, don't worry about special
// key processing.
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.shouldHandleGlobalKey(keyCode)) {
// Dispatch if global key defined dispatchWhenNonInteractive.
if (!interactive && isWakeKey && down
&& mGlobalKeyManager.shouldDispatchFromNonInteractive(keyCode)) {
mGlobalKeyManager.setBeganFromNonInteractive();
result = ACTION_PASS_TO_USER;
// Since we're dispatching the input, reset the pending key
mPendingWakeKey = PENDING_KEY_NULL;
}
if (isWakeKey) {
wakeUpFromWakeKey(event);
}
return result;
}
// Alternate TV power to power key for Android TV device.
final HdmiControlManager hdmiControlManager = getHdmiControlManager();
if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback
&& (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) {
event = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), KeyEvent.KEYCODE_POWER,
event.getRepeatCount(), event.getMetaState(),
event.getDeviceId(), event.getScanCode(),
event.getFlags(), event.getSource(), event.getDisplayId(), null);
return interceptKeyBeforeQueueing(event, policyFlags);
}
// This could prevent some wrong state in multi-displays environment,
// the default display may turned off but interactive is true.
final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
final boolean isDefaultDisplayAwake = mDefaultDisplayPolicy.isAwake();
final boolean interactiveAndAwake = interactive && isDefaultDisplayAwake;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
handleKeyGesture(event, interactiveAndAwake, isDefaultDisplayOn);
}
// Enable haptics if down and virtual key without multiple repetitions. If this is a hard
// virtual key such as a navigation bar button, only vibrate if flag is enabled.
final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0);
boolean useHapticFeedback = down
&& (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
&& (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
&& event.getRepeatCount() == 0;
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.BACK);
if (down) {
mBackKeyHandled = false;
} else {
if (!hasLongPressOnBackBehavior()) {
mBackKeyHandled |= backKeyPress();
}
// Don't pass back press to app if we've already handled it via long press
if (mBackKeyHandled) {
result &= ~ACTION_PASS_TO_USER;
}
}
break;
}
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
logKeyboardSystemsEventOnActionDown(event,
KeyboardLogEvent.getVolumeEvent(keyCode));
if (down) {
sendSystemKeyToStatusBarAsync(event);
NotificationManager nm = getNotificationService();
if (nm != null && !mHandleVolumeKeysInWM) {
nm.silenceNotificationSound();
}
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null && !mHandleVolumeKeysInWM) {
// When {@link #mHandleVolumeKeysInWM} is set, volume key events
// should be dispatched to WM.
if (telecomManager.isRinging()) {
// If an incoming call is ringing, either VOLUME key means
// "silence ringer". We handle these keys here, rather than
// in the InCallScreen, to make sure we'll respond to them
// even if the InCallScreen hasn't come to the foreground yet.
// Look for the DOWN event here, to agree with the "fallback"
// behavior in the InCallScreen.
Log.i(TAG, "interceptKeyBeforeQueueing:"
+ " VOLUME key-down while ringing: Silence ringer!");
// Silence the ringer. (It's safe to call this
// even if the ringer has already been silenced.)
telecomManager.silenceRinger();
// And *don't* pass this key thru to the current activity
// (which is probably the InCallScreen.)
result &= ~ACTION_PASS_TO_USER;
break;
}
}
int audioMode = AudioManager.MODE_NORMAL;
try {
audioMode = getAudioService().getMode();
} catch (Exception e) {
Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
}
boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
audioMode == AudioManager.MODE_IN_COMMUNICATION;
if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
// If we are in call but we decided not to pass the key to
// the application, just pass it to the session service.
MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
break;
}
}
if (mUseTvRouting || mHandleVolumeKeysInWM) {
// Defer special key handlings to
// {@link interceptKeyBeforeDispatching()}.
result |= ACTION_PASS_TO_USER;
} else if ((result & ACTION_PASS_TO_USER) == 0) {
// If we aren't passing to the user and no one else
// handled it send it to the session manager to
// figure out.
MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
}
break;
}
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
hungUp = telecomManager.endCall();
}
if (interactive && !hungUp) {
mEndCallKeyHandled = false;
mHandler.postDelayed(mEndCallLongPress,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
} else {
mEndCallKeyHandled = true;
}
} else {
if (!mEndCallKeyHandled) {
mHandler.removeCallbacks(mEndCallLongPress);
if (!canceled) {
if ((mEndcallBehavior
& Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
if (goHome()) {
break;
}
}
if ((mEndcallBehavior
& Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
sleepDefaultDisplay(event.getEventTime(),
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
isWakeKey = false;
}
}
}
}
break;
}
case KeyEvent.KEYCODE_TV_POWER: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down && hdmiControlManager != null) {
hdmiControlManager.toggleAndFollowTvPower();
}
break;
}
case KeyEvent.KEYCODE_POWER: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER);
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
// Any activity on the power button stops the accessibility shortcut
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactiveAndAwake);
} else {
interceptPowerKeyUp(event, canceled);
}
break;
}
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
// fall through
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
// fall through
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
// fall through
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SYSTEM_NAVIGATION);
result &= ~ACTION_PASS_TO_USER;
interceptSystemNavigationKey(event);
break;
}
case KeyEvent.KEYCODE_SLEEP: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!mPowerManager.isInteractive()) {
useHapticFeedback = false; // suppress feedback if already non-interactive
}
if (down) {
sleepPress();
} else {
sleepRelease(event.getEventTime());
}
sendSystemKeyToStatusBarAsync(event);
break;
}
case KeyEvent.KEYCODE_SOFT_SLEEP: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!down) {
mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
}
sendSystemKeyToStatusBarAsync(event);
break;
}
case KeyEvent.KEYCODE_WAKEUP: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.WAKEUP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
break;
}
case KeyEvent.KEYCODE_MUTE:
result &= ~ACTION_PASS_TO_USER;
if (down && event.getRepeatCount() == 0) {
logKeyboardSystemsEvent(event, KeyboardLogEvent.SYSTEM_MUTE);
toggleMicrophoneMuteFromKey();
}
break;
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
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: {
logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.MEDIA_KEY);
if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
// If the global session is active pass all media keys to it
// instead of the active window.
result &= ~ACTION_PASS_TO_USER;
}
if ((result & ACTION_PASS_TO_USER) == 0) {
// Only do this if we would otherwise not pass it to the user. In that
// case, the PhoneWindow class will do the same thing, except it will
// only do it if the showing app doesn't process the key on its own.
// Note that we need to make a copy of the key event here because the
// original key event will be recycled when we return.
mBroadcastWakeLock.acquire();
Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,
new KeyEvent(event));
msg.setAsynchronous(true);
msg.sendToTarget();
}
break;
}
case KeyEvent.KEYCODE_CALL: {
if (down) {
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null) {
if (telecomManager.isRinging()) {
Log.i(TAG, "interceptKeyBeforeQueueing:"
+ " CALL key-down while ringing: Answer the call!");
telecomManager.acceptRingingCall();
// And *don't* pass this key thru to the current activity
// (which is presumably the InCallScreen.)
result &= ~ACTION_PASS_TO_USER;
}
}
}
break;
}
case KeyEvent.KEYCODE_ASSIST: {
final boolean longPressed = event.getRepeatCount() > 0;
if (down && !longPressed) {
Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
0 /* unused */, event.getEventTime() /* eventTime */);
msg.setAsynchronous(true);
msg.sendToTarget();
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
}
result &= ~ACTION_PASS_TO_USER;
break;
}
case KeyEvent.KEYCODE_VOICE_ASSIST: {
if (!down) {
mBroadcastWakeLock.acquire();
Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
msg.setAsynchronous(true);
msg.sendToTarget();
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT);
}
result &= ~ACTION_PASS_TO_USER;
break;
}
case KeyEvent.KEYCODE_WINDOW: {
if (mShortPressOnWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
if (mPictureInPictureVisible) {
// Consumes the key only if picture-in-picture is visible to show
// picture-in-picture control menu. This gives a chance to the foreground
// activity to customize PIP key behavior.
if (!down) {
showPictureInPictureMenu(event);
}
result &= ~ACTION_PASS_TO_USER;
}
}
break;
}
case KeyEvent.KEYCODE_VIDEO_APP_1:
case KeyEvent.KEYCODE_VIDEO_APP_2:
case KeyEvent.KEYCODE_VIDEO_APP_3:
case KeyEvent.KEYCODE_VIDEO_APP_4:
case KeyEvent.KEYCODE_VIDEO_APP_5:
case KeyEvent.KEYCODE_VIDEO_APP_6:
case KeyEvent.KEYCODE_VIDEO_APP_7:
case KeyEvent.KEYCODE_VIDEO_APP_8:
case KeyEvent.KEYCODE_FEATURED_APP_1:
case KeyEvent.KEYCODE_FEATURED_APP_2:
case KeyEvent.KEYCODE_FEATURED_APP_3:
case KeyEvent.KEYCODE_FEATURED_APP_4:
case KeyEvent.KEYCODE_DEMO_APP_1:
case KeyEvent.KEYCODE_DEMO_APP_2:
case KeyEvent.KEYCODE_DEMO_APP_3:
case KeyEvent.KEYCODE_DEMO_APP_4: {
// Just drop if keys are not intercepted for direct key.
result &= ~ACTION_PASS_TO_USER;
break;
}
case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
if (mStylusButtonsEnabled) {
sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;
break;
}
case KeyEvent.KEYCODE_MACRO_1:
case KeyEvent.KEYCODE_MACRO_2:
case KeyEvent.KEYCODE_MACRO_3:
case KeyEvent.KEYCODE_MACRO_4:
result &= ~ACTION_PASS_TO_USER;
break;
}
if (useHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Virtual Key - Press");
}
if (isWakeKey) {
wakeUpFromWakeKey(event);
}
// If the key event is targeted to a specific display, then the user is interacting with
// that display. Therefore, try to give focus to the display that the user is interacting
// with.
if ((result & ACTION_PASS_TO_USER) != 0 && displayId != INVALID_DISPLAY
&& displayId != mTopFocusedDisplayId) {
// An event is targeting a non-focused display. Move the display to top so that
// it can become the focused display to interact with the user.
// This should be done asynchronously, once the focus logic is fully moved to input
// from windowmanager. Currently, we need to ensure the setInputWindows completes,
// which would force the focus event to be queued before the current key event.
// TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead
Log.i(TAG, "Attempting to move non-focused display " + displayId + " to top "
+ "because a key is targeting it");
mWindowManagerFuncs.moveDisplayToTopIfAllowed(displayId);
}
return result;
}
private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) {
if (mKeyCombinationManager.interceptKey(event, interactive)) {
// handled by combo keys manager.
mSingleKeyGestureDetector.reset();
return;
}
if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
mPowerKeyHandled = handleCameraGesture(event, interactive);
if (mPowerKeyHandled) {
// handled by camera gesture.
mSingleKeyGestureDetector.reset();
return;
}
}
mSingleKeyGestureDetector.interceptKey(event, interactive, defaultDisplayOn);
}
// The camera gesture will be detected by GestureLauncherService.
private boolean handleCameraGesture(KeyEvent event, boolean interactive) {
// camera gesture.
if (mGestureLauncherService == null) {
return false;
}
mCameraGestureTriggered = false;
final MutableBoolean outLaunched = new MutableBoolean(false);
final boolean intercept =
mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
if (!outLaunched.value) {
// If GestureLauncherService intercepted the power key, but didn't launch camera app,
// we should still return the intercept result. This prevents the single key gesture
// detector from processing the power key later on.
return intercept;
}
mCameraGestureTriggered = true;
if (mRequestedOrSleepingDefaultDisplay) {
mCameraGestureTriggeredDuringGoingToSleep = true;
// Wake device up early to prevent display doing redundant turning off/on stuff.
wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
PowerManager.WAKE_REASON_CAMERA_LAUNCH,
"android.policy:CAMERA_GESTURE_PREVENT_LOCK");
}
return true;
}
/**
* Handle statusbar expansion events.
* @param event
*/
private void interceptSystemNavigationKey(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (!mAccessibilityManager.isEnabled()
|| !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
if (mSystemNavigationKeysEnabled) {
sendSystemKeyToStatusBarAsync(event);
}
}
}
}
/**
* Notify the StatusBar that a system key was pressed.
*/
private void sendSystemKeyToStatusBar(KeyEvent key) {
IStatusBarService statusBar = getStatusBarService();
if (statusBar != null) {
try {
statusBar.handleSystemKey(key);
} catch (RemoteException e) {
// Oh well.
}
}
}
/**
* Notify the StatusBar that a system key was pressed without blocking the current thread.
*/
private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) {
// Make a copy because the event may be recycled.
Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, KeyEvent.obtain(keyEvent));
message.setAsynchronous(true);
mHandler.sendMessage(message);
}
/**
* Returns true if the key can have global actions attached to it.
* We reserve all power management keys for the system since they require
* very careful handling.
*/
private static boolean isValidGlobalKey(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_POWER:
case KeyEvent.KEYCODE_WAKEUP:
case KeyEvent.KEYCODE_SLEEP:
return false;
default:
return true;
}
}
/**
* When the screen is off we ignore some keys that might otherwise typically
* be considered wake keys. We filter them out here.
*
* {@link KeyEvent#KEYCODE_POWER} is notably absent from this list because it
* is always considered a wake key.
*/
private boolean isWakeKeyWhenScreenOff(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_CENTER:
return mWakeOnDpadKeyPress;
case KeyEvent.KEYCODE_ASSIST:
return mWakeOnAssistKeyPress;
case KeyEvent.KEYCODE_BACK:
return mWakeOnBackKeyPress;
case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
return mStylusButtonsEnabled;
}
return true;
}
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
int policyFlags) {
if ((policyFlags & FLAG_WAKE) != 0) {
if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
}
if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
return ACTION_PASS_TO_USER;
}
// If we have not passed the action up and we are in theater mode without dreaming,
// there will be no dream to intercept the touch and wake into ambient. The device should
// wake up in this case.
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
}
return 0;
}
private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
// Apply the default display policy to unknown displays as well.
final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
|| displayId == INVALID_DISPLAY;
final Display display = isDefaultDisplay
? mDefaultDisplay
: mDisplayManager.getDisplay(displayId);
final boolean displayOff = (display == null
|| display.getState() == STATE_OFF);
if (displayOff) {
return false;
}
// Send events to keyguard while the screen is on and it's showing.
if (isKeyguardShowingAndNotOccluded()) {
return true;
}
// TODO(b/123372519): Refine when dream can support multi display.
if (isDefaultDisplay) {
// Send events to a dozing dream since the dream is in control of the state of the
// screen.
IDreamManager dreamManager = getDreamManager();
try {
if (dreamManager != null && dreamManager.isDreaming()) {
return true;
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when checking if dreaming", e);
}
}
// Otherwise, consume events since the user can't see what is being
// interacted with.
return false;
}
// pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,
// KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE
private void dispatchDirectAudioEvent(KeyEvent event) {
// When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
// or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
HdmiControlManager hdmiControlManager = getHdmiControlManager();
if (null != hdmiControlManager
&& !hdmiControlManager.getSystemAudioMode()
&& shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
if (audioSystemClient != null) {
audioSystemClient.sendKeyEvent(
event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
return;
}
}
try {
getAudioService().handleVolumeKey(event, mUseTvRouting,
mContext.getOpPackageName(), TAG);
} catch (Exception e) {
Log.e(TAG, "Error dispatching volume key in handleVolumeKey for event:"
+ event, e);
}
}
@Nullable
private HdmiControlManager getHdmiControlManager() {
if (!mHasFeatureHdmiCec) {
return null;
}
return (HdmiControlManager) mContext.getSystemService(HdmiControlManager.class);
}
private boolean shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff() {
return RoSystemProperties.CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF;
}
void dispatchMediaKeyWithWakeLock(KeyEvent event) {
if (DEBUG_INPUT) {
Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
}
if (mHavePendingMediaKeyRepeatWithWakeLock) {
if (DEBUG_INPUT) {
Slog.d(TAG, "dispatchMediaKeyWithWakeLock: canceled repeat");
}
mHandler.removeMessages(MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK);
mHavePendingMediaKeyRepeatWithWakeLock = false;
mBroadcastWakeLock.release(); // pending repeat was holding onto the wake lock
}
dispatchMediaKeyWithWakeLockToAudioService(event);
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0) {
mHavePendingMediaKeyRepeatWithWakeLock = true;
Message msg = mHandler.obtainMessage(
MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK, event);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, ViewConfiguration.getKeyRepeatTimeout());
} else {
mBroadcastWakeLock.release();
}
}
void dispatchMediaKeyRepeatWithWakeLock(KeyEvent event) {
mHavePendingMediaKeyRepeatWithWakeLock = false;
KeyEvent repeatEvent = KeyEvent.changeTimeRepeat(event,
SystemClock.uptimeMillis(), 1, event.getFlags() | KeyEvent.FLAG_LONG_PRESS);
if (DEBUG_INPUT) {
Slog.d(TAG, "dispatchMediaKeyRepeatWithWakeLock: " + repeatEvent);
}
dispatchMediaKeyWithWakeLockToAudioService(repeatEvent);
mBroadcastWakeLock.release();
}
void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {
if (mActivityManagerInternal.isSystemReady()) {
MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
}
}
void launchVoiceAssistWithWakeLock() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
final Intent voiceIntent;
if (!keyguardOn()) {
voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
} else {
DeviceIdleManager dim = mContext.getSystemService(DeviceIdleManager.class);
if (dim != null) {
dim.endIdle("voice-search");
}
voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true);
}
startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF);
mBroadcastWakeLock.release();
}
BroadcastReceiver mDockReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED));
} else {
try {
IUiModeManager uiModeService = IUiModeManager.Stub.asInterface(
ServiceManager.getService(Context.UI_MODE_SERVICE));
mUiMode = uiModeService.getCurrentModeType();
} catch (RemoteException e) {
}
}
updateRotation(true);
mDefaultDisplayRotation.updateOrientationListener();
}
};
BroadcastReceiver mMultiuserReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
// tickle the settings observer: this first ensures that we're
// observing the relevant settings for the newly-active user,
// and then updates our own bookkeeping based on the now-
// current user.
mSettingsObserver.onChange(false);
mDefaultDisplayRotation.onUserSwitch();
mWindowManagerFuncs.onUserSwitched();
}
}
};
@Override
public void startedWakingUpGlobal(@WakeReason int reason) {
}
@Override
public void finishedWakingUpGlobal(@WakeReason int reason) {
}
@Override
public void startedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason) {
mDeviceGoingToSleep = true;
}
@Override
public void finishedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason) {
mDeviceGoingToSleep = false;
}
// Called on the PowerManager's Notifier thread.
@Override
public void startedGoingToSleep(int displayGroupId,
@PowerManager.GoToSleepReason int pmSleepReason) {
if (DEBUG_WAKEUP) {
Slog.i(TAG, "Started going to sleep... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.offReasonToString(
WindowManagerPolicyConstants.translateSleepReasonToOffReason(
pmSleepReason)) + ")");
}
if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
return;
}
mRequestedOrSleepingDefaultDisplay = true;
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
}
}
// Called on the PowerManager's Notifier thread.
@Override
public void finishedGoingToSleep(int displayGroupId,
@PowerManager.GoToSleepReason int pmSleepReason) {
if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
return;
}
EventLogTags.writeScreenToggled(0);
if (DEBUG_WAKEUP) {
Slog.i(TAG, "Finished going to sleep... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.offReasonToString(
WindowManagerPolicyConstants.translateSleepReasonToOffReason(
pmSleepReason)) + ")");
}
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(false);
// We must get this work done here because the power manager will drop
// the wake lock and let the system suspend once this function returns.
synchronized (mLock) {
updateWakeGestureListenerLp();
updateLockScreenTimeout();
}
mDefaultDisplayRotation.updateOrientationListener();
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason,
mCameraGestureTriggeredDuringGoingToSleep);
}
if (mDisplayFoldController != null) {
mDisplayFoldController.finishedGoingToSleep();
}
mCameraGestureTriggeredDuringGoingToSleep = false;
mCameraGestureTriggered = false;
}
// Called on the PowerManager's Notifier thread.
@Override
public void startedWakingUp(int displayGroupId, @WakeReason int pmWakeReason) {
if (DEBUG_WAKEUP) {
Slog.i(TAG, "Started waking up... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.onReasonToString(
WindowManagerPolicyConstants.translateWakeReasonToOnReason(
pmWakeReason)) + ")");
}
if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
return;
}
EventLogTags.writeScreenToggled(1);
mDefaultDisplayPolicy.setAwake(true);
// Since goToSleep performs these functions synchronously, we must
// do the same here. We cannot post this work to a handler because
// that might cause it to become reordered with respect to what
// may happen in a future call to goToSleep.
synchronized (mLock) {
updateWakeGestureListenerLp();
updateLockScreenTimeout();
}
mDefaultDisplayRotation.updateOrientationListener();
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mCameraGestureTriggered);
}
mCameraGestureTriggered = false;
}
// Called on the PowerManager's Notifier thread.
@Override
public void finishedWakingUp(int displayGroupId, @WakeReason int pmWakeReason) {
if (DEBUG_WAKEUP) {
Slog.i(TAG, "Finished waking up... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.onReasonToString(
WindowManagerPolicyConstants.translateWakeReasonToOnReason(
pmWakeReason)) + ")");
}
if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
return;
}
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedWakingUp();
}
if (mDisplayFoldController != null) {
mDisplayFoldController.finishedWakingUp();
}
}
private boolean shouldWakeUpWithHomeIntent() {
if (mWakeUpToLastStateTimeout <= 0) {
return false;
}
final long sleepDurationRealtime =
mPowerManagerInternal.getLastWakeup().sleepDurationRealtime;
if (DEBUG_WAKEUP) {
Log.i(TAG, "shouldWakeUpWithHomeIntent: sleepDurationRealtime= " + sleepDurationRealtime
+ " mWakeUpToLastStateTimeout= " + mWakeUpToLastStateTimeout);
}
return sleepDurationRealtime > mWakeUpToLastStateTimeout;
}
private void wakeUpFromPowerKey(long eventTime) {
if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
if (shouldWakeUpWithHomeIntent()) {
startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
}
}
}
private void wakeUpFromWakeKey(KeyEvent event) {
if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) {
// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) {
startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true,
PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_WAKE_KEY));
}
}
}
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
String details) {
final boolean theaterModeEnabled = isTheaterModeEnabled();
if (!wakeInTheaterMode && theaterModeEnabled) {
return false;
}
mPowerManager.wakeUp(wakeTime, reason, details);
return true;
}
private void finishKeyguardDrawn() {
if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) {
return;
}
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
}
}
// ... eventually calls finishWindowsDrawn which will finalize our screen turn on
// as well as enabling the orientation change logic/sensor.
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, INVALID_DISPLAY /* cookie */);
mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
MSG_WINDOW_MANAGER_DRAWN_COMPLETE, INVALID_DISPLAY, 0),
WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
}
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurnedOff(int displayId, boolean isSwappingDisplay) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
if (displayId == DEFAULT_DISPLAY) {
updateScreenOffSleepToken(true, isSwappingDisplay);
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.screenTurnedOff();
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOff();
}
}
mDefaultDisplayRotation.updateOrientationListener();
reportScreenStateToVrManager(false);
}
}
private long getKeyguardDrawnTimeout() {
final boolean bootCompleted =
LocalServices.getService(SystemServiceManager.class).isBootCompleted();
// Set longer timeout if it has not booted yet to prevent showing empty window.
return bootCompleted ? mKeyguardDrawnTimeout : 5000;
}
@Nullable
private WallpaperManagerInternal getWallpaperManagerInternal() {
if (mWallpaperManagerInternal == null) {
mWallpaperManagerInternal = LocalServices.getService(WallpaperManagerInternal.class);
}
return mWallpaperManagerInternal;
}
private void reportScreenTurningOnToWallpaper(int displayId) {
WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
if (wallpaperManagerInternal != null) {
wallpaperManagerInternal.onScreenTurningOn(displayId);
}
}
private void reportScreenTurnedOnToWallpaper(int displayId) {
WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
if (wallpaperManagerInternal != null) {
wallpaperManagerInternal.onScreenTurnedOn(displayId);
}
}
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");
reportScreenTurningOnToWallpaper(displayId);
if (displayId == DEFAULT_DISPLAY) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
0 /* cookie */);
updateScreenOffSleepToken(false /* acquire */, false /* isSwappingDisplay */);
mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
mBootAnimationDismissable = false;
synchronized (mLock) {
if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
getKeyguardDrawnTimeout());
mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
} else {
if (DEBUG_WAKEUP) Slog.d(TAG,
"null mKeyguardDelegate: setting mKeyguardDrawComplete.");
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
}
}
} else {
mScreenOnListeners.put(displayId, screenOnListener);
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
MSG_WINDOW_MANAGER_DRAWN_COMPLETE, displayId, 0),
WAITING_FOR_DRAWN_TIMEOUT, displayId);
}
}
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurnedOn(int displayId) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on...");
reportScreenTurnedOnToWallpaper(displayId);
if (displayId != DEFAULT_DISPLAY) {
return;
}
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOn();
}
}
reportScreenStateToVrManager(true);
}
@Override
public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
mWindowManagerFuncs.screenTurningOff(displayId, screenOffListener);
if (displayId != DEFAULT_DISPLAY) {
return;
}
mRequestedOrSleepingDefaultDisplay = true;
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurningOff();
}
}
}
private void reportScreenStateToVrManager(boolean isScreenOn) {
if (mVrManagerInternal == null) {
return;
}
mVrManagerInternal.onScreenStateChanged(isScreenOn);
}
private void finishWindowsDrawn(int displayId) {
if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY) {
final ScreenOnListener screenOnListener = mScreenOnListeners.removeReturnOld(displayId);
if (screenOnListener != null) {
screenOnListener.onScreenOn();
}
return;
}
if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
return;
}
finishScreenTurningOn();
}
private void finishScreenTurningOn() {
// We have just finished drawing screen content. Since the orientation listener
// gets only installed when all windows are drawn, we try to install it again.
mDefaultDisplayRotation.updateOrientationListener();
final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
return; // Spurious or not ready yet.
}
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
enableScreen(listener, true /* report */);
}
private void enableScreen(ScreenOnListener listener, boolean report) {
final boolean enableScreen;
final boolean awake = mDefaultDisplayPolicy.isAwake();
synchronized (mLock) {
// Remember the first time we draw the keyguard so we know when we're done with
// the main part of booting and can enable the screen and hide boot messages.
if (!mKeyguardDrawnOnce && awake) {
mKeyguardDrawnOnce = true;
enableScreen = true;
if (mBootMessageNeedsHiding) {
mBootMessageNeedsHiding = false;
hideBootMessages();
}
} else {
enableScreen = false;
}
}
if (report) {
if (listener != null) {
listener.onScreenOn();
}
}
if (enableScreen) {
mWindowManagerFuncs.enableScreenIfNeeded();
}
}
private void handleHideBootMessage() {
synchronized (mLock) {
if (!mKeyguardDrawnOnce) {
mBootMessageNeedsHiding = true;
return; // keyguard hasn't drawn the first time yet, not done booting
}
}
if (mBootMsgDialog != null) {
if (DEBUG_WAKEUP) Slog.d(TAG, "handleHideBootMessage: dismissing");
mBootMsgDialog.dismiss();
mBootMsgDialog = null;
}
}
@Override
public boolean isScreenOn() {
return mDefaultDisplayPolicy.isScreenOnEarly();
}
@Override
public boolean okToAnimate(boolean ignoreScreenOn) {
return (ignoreScreenOn || isScreenOn()) && !mDeviceGoingToSleep;
}
/** {@inheritDoc} */
@Override
public void enableKeyguard(boolean enabled) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.setKeyguardEnabled(enabled);
}
}
/** {@inheritDoc} */
@Override
public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.verifyUnlock(callback);
}
}
@Override
public boolean isKeyguardShowing() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isShowing();
}
@Override
public boolean isKeyguardShowingAndNotOccluded() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isShowing() && !isKeyguardOccluded();
}
@Override
public boolean isKeyguardTrustedLw() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isTrusted();
}
/** {@inheritDoc} */
@Override
public boolean isKeyguardLocked() {
return keyguardOn();
}
/** {@inheritDoc} */
@Override
public boolean isKeyguardSecure(int userId) {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isSecure(userId);
}
/** {@inheritDoc} */
@Override
public boolean isKeyguardOccluded() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isOccluded();
}
/** {@inheritDoc} */
@Override
public boolean inKeyguardRestrictedKeyInputMode() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isInputRestricted();
}
/** {@inheritDoc} */
@Override
public boolean isKeyguardUnoccluding() {
return keyguardOn() && !mWindowManagerFuncs.isAppTransitionStateIdle();
}
@Override
public void dismissKeyguardLw(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.dismissKeyguardLw");
// ask the keyguard to prompt the user to authenticate if necessary
mKeyguardDelegate.dismiss(callback, message);
} else if (callback != null) {
try {
callback.onDismissError();
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call callback", e);
}
}
}
@Override
public boolean isKeyguardDrawnLw() {
synchronized (mLock) {
return mKeyguardDrawnOnce;
}
}
@Override
public void startKeyguardExitAnimation(long startTime) {
if (mKeyguardDelegate != null) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
mKeyguardDelegate.startKeyguardExitAnimation(startTime);
}
}
void sendCloseSystemWindows() {
PhoneWindow.sendCloseSystemWindows(mContext, null);
}
void sendCloseSystemWindows(String reason) {
PhoneWindow.sendCloseSystemWindows(mContext, reason);
}
@Override
public void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
if (safeMode) {
performHapticFeedback(HapticFeedbackConstants.SAFE_MODE_ENABLED, true,
"Safe Mode Enabled");
}
}
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
return;
}
mKeyguardBound = true;
}
mKeyguardDelegate.bindService(mContext);
}
@Override
public void onSystemUiStarted() {
bindKeyguard();
}
/** {@inheritDoc} */
@Override
public void systemReady() {
// In normal flow, systemReady is called before other system services are ready.
// So it is better not to bind keyguard here.
mKeyguardDelegate.onSystemReady();
mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
if (mVrManagerInternal != null) {
mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
}
readCameraLensCoverState();
updateUiMode();
mDefaultDisplayRotation.updateOrientationListener();
synchronized (mLock) {
mSystemReady = true;
updateSettings(mHandler);
// If this happens, for whatever reason, systemReady came later than systemBooted.
// And keyguard should be already bound from systemBooted
if (mSystemBooted) {
mKeyguardDelegate.onBootCompleted();
}
}
mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class);
mGestureLauncherService = LocalServices.getService(GestureLauncherService.class);
}
/** {@inheritDoc} */
@Override
public void systemBooted() {
bindKeyguard();
synchronized (mLock) {
mSystemBooted = true;
if (mSystemReady) {
mKeyguardDelegate.onBootCompleted();
}
}
mSideFpsEventHandler.onFingerprintSensorReady();
startedWakingUp(Display.DEFAULT_DISPLAY_GROUP, PowerManager.WAKE_REASON_UNKNOWN);
finishedWakingUp(Display.DEFAULT_DISPLAY_GROUP, PowerManager.WAKE_REASON_UNKNOWN);
int defaultDisplayState = mDisplayManager.getDisplay(DEFAULT_DISPLAY).getState();
boolean defaultDisplayOn = defaultDisplayState == Display.STATE_ON;
boolean defaultScreenTurningOn = mDefaultDisplayPolicy.getScreenOnListener() != null;
if (defaultDisplayOn || defaultScreenTurningOn) {
// Now that system is booted, wait for keyguard and windows to be drawn before
// updating the orientation listener, stopping the boot animation and enabling screen.
screenTurningOn(DEFAULT_DISPLAY, mDefaultDisplayPolicy.getScreenOnListener());
screenTurnedOn(DEFAULT_DISPLAY);
} else {
// We're not turning the screen on, so don't wait for keyguard to be drawn
// to dismiss the boot animation and finish booting
mBootAnimationDismissable = true;
enableScreen(null, false /* report */);
}
}
@Override
public boolean canDismissBootAnimation() {
// Allow to dismiss the boot animation if the keyguard has finished drawing,
// or mBootAnimationDismissable has been set
return mDefaultDisplayPolicy.isKeyguardDrawComplete() || mBootAnimationDismissable;
}
ProgressDialog mBootMsgDialog = null;
/** {@inheritDoc} */
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
mHandler.post(new Runnable() {
@Override public void run() {
if (mBootMsgDialog == null) {
int theme;
if (mPackageManager.hasSystemFeature(FEATURE_LEANBACK)) {
theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
} else {
theme = 0;
}
mBootMsgDialog = new ProgressDialog(mContext, theme) {
// This dialog will consume all events coming in to
// it, to avoid it trying to do things too early in boot.
@Override public boolean dispatchKeyEvent(KeyEvent event) {
return true;
}
@Override public boolean dispatchKeyShortcutEvent(KeyEvent event) {
return true;
}
@Override public boolean dispatchTouchEvent(MotionEvent ev) {
return true;
}
@Override public boolean dispatchTrackballEvent(MotionEvent ev) {
return true;
}
@Override public boolean dispatchGenericMotionEvent(MotionEvent ev) {
return true;
}
@Override public boolean dispatchPopulateAccessibilityEvent(
AccessibilityEvent event) {
return true;
}
};
if (mPackageManager.isDeviceUpgrading()) {
mBootMsgDialog.setTitle(R.string.android_upgrading_title);
} else {
mBootMsgDialog.setTitle(R.string.android_start_title);
}
mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mBootMsgDialog.setIndeterminate(true);
mBootMsgDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_BOOT_PROGRESS);
mBootMsgDialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
mBootMsgDialog.getWindow().setDimAmount(1);
WindowManager.LayoutParams lp = mBootMsgDialog.getWindow().getAttributes();
lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
lp.setFitInsetsTypes(0 /* types */);
mBootMsgDialog.getWindow().setAttributes(lp);
mBootMsgDialog.setCancelable(false);
mBootMsgDialog.show();
}
mBootMsgDialog.setMessage(msg);
}
});
}
/** {@inheritDoc} */
@Override
public void hideBootMessages() {
mHandler.sendEmptyMessage(MSG_HIDE_BOOT_MESSAGE);
}
/** {@inheritDoc} */
@Override
public void userActivity(int displayGroupId, int event) {
if (displayGroupId == DEFAULT_DISPLAY && event == PowerManager.USER_ACTIVITY_EVENT_TOUCH) {
mDefaultDisplayPolicy.onUserActivityEventTouch();
}
synchronized (mScreenLockTimeout) {
if (mLockScreenTimerActive) {
// reset the timer
mHandler.removeCallbacks(mScreenLockTimeout);
mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
}
}
}
class ScreenLockTimeout implements Runnable {
Bundle options;
@Override
public void run() {
synchronized (this) {
if (localLOGV) Log.v(TAG, "mScreenLockTimeout activating keyguard");
if (mKeyguardDelegate != null) {
mKeyguardDelegate.doKeyguardTimeout(options);
}
mLockScreenTimerActive = false;
mLockNowPending = false;
options = null;
}
}
public void setLockOptions(Bundle options) {
this.options = options;
}
}
final ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();
@Override
public void lockNow(Bundle options) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
mHandler.removeCallbacks(mScreenLockTimeout);
if (options != null) {
// In case multiple calls are made to lockNow, we don't wipe out the options
// until the runnable actually executes.
mScreenLockTimeout.setLockOptions(options);
}
mHandler.post(mScreenLockTimeout);
synchronized (mScreenLockTimeout) {
mLockNowPending = true;
}
}
// TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
@Override
public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
if (allow) {
mAllowLockscreenWhenOnDisplays.add(displayId);
} else {
mAllowLockscreenWhenOnDisplays.remove(displayId);
}
updateLockScreenTimeout();
}
private void updateLockScreenTimeout() {
synchronized (mScreenLockTimeout) {
if (mLockNowPending) {
Log.w(TAG, "lockNow pending, ignore updating lockscreen timeout");
return;
}
final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty()
&& mDefaultDisplayPolicy.isAwake()
&& mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId);
if (mLockScreenTimerActive != enable) {
if (enable) {
if (localLOGV) Log.v(TAG, "setting lockscreen timer");
mHandler.removeCallbacks(mScreenLockTimeout); // remove any pending requests
mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
} else {
if (localLOGV) Log.v(TAG, "clearing lockscreen timer");
mHandler.removeCallbacks(mScreenLockTimeout);
}
mLockScreenTimerActive = enable;
}
}
}
// TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
private void updateScreenOffSleepToken(boolean acquire, boolean isSwappingDisplay) {
if (acquire) {
mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY, isSwappingDisplay);
} else {
mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
}
}
/** {@inheritDoc} */
@Override
public void enableScreenAfterBoot() {
readLidState();
applyLidSwitchState();
updateRotation(true);
}
private void applyLidSwitchState() {
final int lidState = mDefaultDisplayPolicy.getLidState();
if (lidState == LID_CLOSED) {
int lidBehavior = getLidBehavior();
switch (lidBehavior) {
case LID_BEHAVIOR_LOCK:
mWindowManagerFuncs.lockDeviceNow();
break;
case LID_BEHAVIOR_SLEEP:
sleepDefaultDisplay(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
case LID_BEHAVIOR_NONE:
// fall through
default:
break;
}
}
synchronized (mLock) {
updateWakeGestureListenerLp();
}
}
void updateUiMode() {
if (mUiModeManager == null) {
mUiModeManager = IUiModeManager.Stub.asInterface(
ServiceManager.getService(Context.UI_MODE_SERVICE));
}
try {
mUiMode = mUiModeManager.getCurrentModeType();
} catch (RemoteException e) {
}
}
@Override
public int getUiMode() {
return mUiMode;
}
void updateRotation(boolean alwaysSendConfiguration) {
mWindowManagerFuncs.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
}
/**
* Return an Intent to launch the currently active dock app as home. Returns
* null if the standard home should be launched, which is the case if any of the following is
* true:
* <ul>
* <li>The device is not in either car mode or desk mode
* <li>The device is in car mode but mEnableCarDockHomeCapture is false
* <li>The device is in desk mode but ENABLE_DESK_DOCK_HOME_CAPTURE is false
* <li>The device is in car mode but there's no CAR_DOCK app with METADATA_DOCK_HOME
* <li>The device is in desk mode but there's no DESK_DOCK app with METADATA_DOCK_HOME
* </ul>
* @return A dock intent.
*/
Intent createHomeDockIntent() {
Intent intent = null;
// What home does is based on the mode, not the dock state. That
// is, when in car mode you should be taken to car home regardless
// of whether we are actually in a car dock.
if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
if (mEnableCarDockHomeCapture) {
intent = mCarDockIntent;
}
} else if (mUiMode == Configuration.UI_MODE_TYPE_DESK) {
if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
intent = mDeskDockIntent;
}
} else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH) {
final int dockMode = mDefaultDisplayPolicy.getDockMode();
if (dockMode == Intent.EXTRA_DOCK_STATE_DESK
|| dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
|| dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK) {
// Always launch dock home from home when watch is docked, if it exists.
intent = mDeskDockIntent;
}
} else if (mUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET) {
if (ENABLE_VR_HEADSET_HOME_CAPTURE) {
intent = mVrHeadsetHomeIntent;
}
}
if (intent == null) {
return null;
}
ActivityInfo ai = null;
ResolveInfo info = mPackageManager.resolveActivityAsUser(
intent,
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
mCurrentUserId);
if (info != null) {
ai = info.activityInfo;
}
if (ai != null
&& ai.metaData != null
&& ai.metaData.getBoolean(Intent.METADATA_DOCK_HOME)) {
intent = new Intent(intent);
intent.setClassName(ai.packageName, ai.name);
return intent;
}
return null;
}
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
String startReason) {
try {
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
if (awakenFromDreams) {
awakenDreams();
}
if (!mHasFeatureAuto && !isUserSetupComplete()) {
Slog.i(TAG, "Not going home because user setup is in progress.");
return;
}
// Start dock.
Intent dock = createHomeDockIntent();
if (dock != null) {
try {
if (fromHomeKey) {
dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
}
startActivityAsUser(dock, UserHandle.CURRENT);
return;
} catch (ActivityNotFoundException e) {
}
}
if (DEBUG_WAKEUP) {
Log.d(TAG, "startDockOrHome: startReason= " + startReason);
}
int userId = mUserManagerInternal.getUserAssignedToDisplay(displayId);
// Start home.
mActivityTaskManagerInternal.startHomeOnDisplay(userId, startReason,
displayId, true /* allowInstrumenting */, fromHomeKey);
}
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams) {
startDockOrHome(displayId, fromHomeKey, awakenFromDreams, /*startReason*/
"startDockOrHome");
}
/**
* goes to the home screen
* @return whether it did anything
*/
boolean goHome() {
if (!isUserSetupComplete()) {
Slog.i(TAG, "Not going home because user setup is in progress.");
return false;
}
if (false) {
// This code always brings home to the front.
startDockOrHome(DEFAULT_DISPLAY, false /*fromHomeKey*/, true /* awakenFromDreams */);
} else {
// This code brings home to the front or, if it is already
// at the front, puts the device to sleep.
try {
if (SystemProperties.getInt("persist.sys.uts-test-mode", 0) == 1) {
/// Roll back EndcallBehavior as the cupcake design to pass P1 lab entry.
Log.d(TAG, "UTS-TEST-MODE");
} else {
ActivityManager.getService().stopAppSwitches();
sendCloseSystemWindows();
final Intent dock = createHomeDockIntent();
if (dock != null) {
int result = ActivityTaskManager.getService()
.startActivityAsUser(null, mContext.getOpPackageName(),
mContext.getAttributionTag(), dock,
dock.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0,
ActivityManager.START_FLAG_ONLY_IF_NEEDED,
null, null, UserHandle.USER_CURRENT);
if (result == ActivityManager.START_RETURN_INTENT_TO_CALLER) {
return false;
}
}
}
int result = ActivityTaskManager.getService()
.startActivityAsUser(null, mContext.getOpPackageName(),
mContext.getAttributionTag(), mHomeIntent,
mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0,
ActivityManager.START_FLAG_ONLY_IF_NEEDED,
null, null, UserHandle.USER_CURRENT);
if (result == ActivityManager.START_RETURN_INTENT_TO_CALLER) {
return false;
}
} catch (RemoteException ex) {
// bummer, the activity manager, which is in this process, is dead
}
}
return true;
}
private boolean isTheaterModeEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0) == 1;
}
private boolean performHapticFeedback(int effectId, boolean always, String reason) {
return performHapticFeedback(Process.myUid(), mContext.getOpPackageName(),
effectId, always, reason);
}
@Override
public boolean isGlobalKey(int keyCode) {
return mGlobalKeyManager.shouldHandleGlobalKey(keyCode);
}
@Override
public boolean performHapticFeedback(int uid, String packageName, int effectId,
boolean always, String reason) {
if (!mVibrator.hasVibrator()) {
return false;
}
VibrationEffect effect =
mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId);
if (effect == null) {
return false;
}
VibrationAttributes attrs =
mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
effectId, /* bypassVibrationIntensitySetting= */ always);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
@Override
public void keepScreenOnStartedLw() {
}
@Override
public void keepScreenOnStoppedLw() {
if (isKeyguardShowingAndNotOccluded()) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
// Use this instead of checking config_showNavigationBar so that it can be consistently
// overridden by qemu.hw.mainkeys in the emulator.
@Override
public boolean hasNavigationBar() {
return mDefaultDisplayPolicy.hasNavigationBar();
}
@Override
public void setDismissImeOnBackKeyPressed(boolean newValue) {
mDismissImeOnBackKeyPressed = newValue;
}
@Override
public void setCurrentUserLw(int newUserId) {
mCurrentUserId = newUserId;
if (mKeyguardDelegate != null) {
mKeyguardDelegate.setCurrentUser(newUserId);
}
if (mAccessibilityShortcutController != null) {
mAccessibilityShortcutController.setCurrentUser(newUserId);
}
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
statusBar.setCurrentUser(newUserId);
}
}
@Override
public void setSwitchingUser(boolean switching) {
mKeyguardDelegate.setSwitchingUser(switching);
}
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
proto.write(SCREEN_ON_FULLY, mDefaultDisplayPolicy.isScreenOnFully());
proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
mDefaultDisplayPolicy.isWindowManagerDrawComplete());
proto.write(KEYGUARD_OCCLUDED, isKeyguardOccluded());
proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
if (mKeyguardDelegate != null) {
mKeyguardDelegate.dumpDebug(proto, KEYGUARD_DELEGATE);
}
proto.end(token);
}
@Override
public void dump(String prefix, PrintWriter pw, String[] args) {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
pw.print(" mSystemBooted="); pw.println(mSystemBooted);
pw.print(prefix); pw.print("mCameraLensCoverState=");
pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
pw.println(mWakeGestureEnabledSetting);
pw.print(prefix);
pw.print("mUiMode=");
pw.print(Configuration.uiModeToString(mUiMode));
pw.print("mEnableCarDockHomeCapture="); pw.println(mEnableCarDockHomeCapture);
pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
pw.print(mLidKeyboardAccessibility);
pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
pw.print(" getLidBehavior="); pw.println(lidBehaviorToString(getLidBehavior()));
pw.print(prefix);
pw.print("mLongPressOnBackBehavior=");
pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior));
pw.print(prefix);
pw.print("mLongPressOnHomeBehavior=");
pw.println(longPressOnHomeBehaviorToString(mLongPressOnHomeBehavior));
pw.print(prefix);
pw.print("mDoubleTapOnHomeBehavior=");
pw.println(doubleTapOnHomeBehaviorToString(mDoubleTapOnHomeBehavior));
pw.print(prefix);
pw.print("mShortPressOnPowerBehavior=");
pw.println(shortPressOnPowerBehaviorToString(mShortPressOnPowerBehavior));
pw.print(prefix);
pw.print("mLongPressOnPowerBehavior=");
pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
pw.print(prefix);
pw.print("mShortPressOnSettingsBehavior=");
pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior));
pw.print(prefix);
pw.print("mLongPressOnPowerAssistantTimeoutMs=");
pw.println(mLongPressOnPowerAssistantTimeoutMs);
pw.print(prefix);
pw.print("mVeryLongPressOnPowerBehavior=");
pw.println(veryLongPressOnPowerBehaviorToString(mVeryLongPressOnPowerBehavior));
pw.print(prefix);
pw.print("mDoublePressOnPowerBehavior=");
pw.println(multiPressOnPowerBehaviorToString(mDoublePressOnPowerBehavior));
pw.print(prefix);
pw.print("mTriplePressOnPowerBehavior=");
pw.println(multiPressOnPowerBehaviorToString(mTriplePressOnPowerBehavior));
pw.print(prefix);
pw.print("mSupportShortPressPowerWhenDefaultDisplayOn=");
pw.println(mSupportShortPressPowerWhenDefaultDisplayOn);
pw.print(prefix);
pw.print("mPowerVolUpBehavior=");
pw.println(powerVolumeUpBehaviorToString(mPowerVolUpBehavior));
pw.print(prefix);
pw.print("mShortPressOnSleepBehavior=");
pw.println(shortPressOnSleepBehaviorToString(mShortPressOnSleepBehavior));
pw.print(prefix);
pw.print("mShortPressOnWindowBehavior=");
pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior));
pw.print(prefix);
pw.print("mShortPressOnStemPrimaryBehavior=");
pw.println(shortPressOnStemPrimaryBehaviorToString(
mShortPressOnStemPrimaryBehavior));
pw.print(prefix);
pw.print("mDoublePressOnStemPrimaryBehavior=");
pw.println(doublePressOnStemPrimaryBehaviorToString(
mDoublePressOnStemPrimaryBehavior));
pw.print(prefix);
pw.print("mTriplePressOnStemPrimaryBehavior=");
pw.println(triplePressOnStemPrimaryBehaviorToString(
mTriplePressOnStemPrimaryBehavior));
pw.print(prefix);
pw.print("mLongPressOnStemPrimaryBehavior=");
pw.println(longPressOnStemPrimaryBehaviorToString(
mLongPressOnStemPrimaryBehavior));
pw.print(prefix);
pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
pw.print(prefix);
pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
pw.print(prefix);
pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
pw.print(" mIncallPowerBehavior=");
pw.println(incallPowerBehaviorToString(mIncallPowerBehavior));
pw.print(prefix);
pw.print("mIncallBackBehavior=");
pw.print(incallBackBehaviorToString(mIncallBackBehavior));
pw.print(" mEndcallBehavior=");
pw.println(endcallBehaviorToString(mEndcallBehavior));
pw.print(prefix);
// TODO(b/117479243): handle it in InputPolicy
pw.println("mDisplayHomeButtonHandlers=");
for (int i = 0; i < mDisplayHomeButtonHandlers.size(); i++) {
final int key = mDisplayHomeButtonHandlers.keyAt(i);
pw.print(prefix); pw.print(" "); pw.println(mDisplayHomeButtonHandlers.get(key));
}
pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(isKeyguardOccluded());
pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays=");
pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
pw.print(prefix); pw.print("mKidsModeEnabled="); pw.println(mKidsModeEnabled);
mHapticFeedbackVibrationProvider.dump(prefix, pw);
mGlobalKeyManager.dump(prefix, pw);
mKeyCombinationManager.dump(prefix, pw);
mSingleKeyGestureDetector.dump(prefix, pw);
mDeferredKeyActionExecutor.dump(prefix, pw);
if (mWakeGestureListener != null) {
mWakeGestureListener.dump(pw, prefix);
}
if (mBurnInProtectionHelper != null) {
mBurnInProtectionHelper.dump(prefix, pw);
}
if (mKeyguardDelegate != null) {
mKeyguardDelegate.dump(prefix, pw);
}
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
}
private static String endcallBehaviorToString(int behavior) {
StringBuilder sb = new StringBuilder();
if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
sb.append("home|");
}
if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
sb.append("sleep|");
}
final int N = sb.length();
if (N == 0) {
return "<nothing>";
} else {
// Chop off the trailing '|'
return sb.substring(0, N - 1);
}
}
private static String incallPowerBehaviorToString(int behavior) {
if ((behavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0) {
return "hangup";
} else {
return "sleep";
}
}
private static String incallBackBehaviorToString(int behavior) {
if ((behavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0) {
return "hangup";
} else {
return "<nothing>";
}
}
private static String longPressOnBackBehaviorToString(int behavior) {
switch (behavior) {
case LONG_PRESS_BACK_NOTHING:
return "LONG_PRESS_BACK_NOTHING";
case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
return "LONG_PRESS_BACK_GO_TO_VOICE_ASSIST";
default:
return Integer.toString(behavior);
}
}
private static String longPressOnHomeBehaviorToString(int behavior) {
switch (behavior) {
case LONG_PRESS_HOME_NOTHING:
return "LONG_PRESS_HOME_NOTHING";
case LONG_PRESS_HOME_ALL_APPS:
return "LONG_PRESS_HOME_ALL_APPS";
case LONG_PRESS_HOME_ASSIST:
return "LONG_PRESS_HOME_ASSIST";
case LONG_PRESS_HOME_NOTIFICATION_PANEL:
return "LONG_PRESS_HOME_NOTIFICATION_PANEL";
default:
return Integer.toString(behavior);
}
}
private static String doubleTapOnHomeBehaviorToString(int behavior) {
switch (behavior) {
case DOUBLE_TAP_HOME_NOTHING:
return "DOUBLE_TAP_HOME_NOTHING";
case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
return "DOUBLE_TAP_HOME_RECENT_SYSTEM_UI";
case DOUBLE_TAP_HOME_PIP_MENU:
return "DOUBLE_TAP_HOME_PIP_MENU";
default:
return Integer.toString(behavior);
}
}
private static String shortPressOnPowerBehaviorToString(int behavior) {
switch (behavior) {
case SHORT_PRESS_POWER_NOTHING:
return "SHORT_PRESS_POWER_NOTHING";
case SHORT_PRESS_POWER_GO_TO_SLEEP:
return "SHORT_PRESS_POWER_GO_TO_SLEEP";
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP";
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME";
case SHORT_PRESS_POWER_GO_HOME:
return "SHORT_PRESS_POWER_GO_HOME";
case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME:
return "SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME";
default:
return Integer.toString(behavior);
}
}
private static String longPressOnPowerBehaviorToString(int behavior) {
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
return "LONG_PRESS_POWER_NOTHING";
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
return "LONG_PRESS_POWER_GLOBAL_ACTIONS";
case LONG_PRESS_POWER_SHUT_OFF:
return "LONG_PRESS_POWER_SHUT_OFF";
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
return "LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM";
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
return "LONG_PRESS_POWER_GO_TO_VOICE_ASSIST";
case LONG_PRESS_POWER_ASSISTANT:
return "LONG_PRESS_POWER_ASSISTANT";
default:
return Integer.toString(behavior);
}
}
private static String shortPressOnSettingsBehaviorToString(int behavior) {
switch (behavior) {
case SHORT_PRESS_SETTINGS_NOTHING:
return "SHORT_PRESS_SETTINGS_NOTHING";
case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL:
return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL";
default:
return Integer.toString(behavior);
}
}
private static String veryLongPressOnPowerBehaviorToString(int behavior) {
switch (behavior) {
case VERY_LONG_PRESS_POWER_NOTHING:
return "VERY_LONG_PRESS_POWER_NOTHING";
case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
return "VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS";
default:
return Integer.toString(behavior);
}
}
private static String powerVolumeUpBehaviorToString(int behavior) {
switch (behavior) {
case POWER_VOLUME_UP_BEHAVIOR_NOTHING:
return "POWER_VOLUME_UP_BEHAVIOR_NOTHING";
case POWER_VOLUME_UP_BEHAVIOR_MUTE:
return "POWER_VOLUME_UP_BEHAVIOR_MUTE";
case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
return "POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS";
default:
return Integer.toString(behavior);
}
}
private static String multiPressOnPowerBehaviorToString(int behavior) {
switch (behavior) {
case MULTI_PRESS_POWER_NOTHING:
return "MULTI_PRESS_POWER_NOTHING";
case MULTI_PRESS_POWER_THEATER_MODE:
return "MULTI_PRESS_POWER_THEATER_MODE";
case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
return "MULTI_PRESS_POWER_BRIGHTNESS_BOOST";
case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
return "MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY";
default:
return Integer.toString(behavior);
}
}
private static String shortPressOnSleepBehaviorToString(int behavior) {
switch (behavior) {
case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
return "SHORT_PRESS_SLEEP_GO_TO_SLEEP";
case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
return "SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME";
default:
return Integer.toString(behavior);
}
}
private static String shortPressOnWindowBehaviorToString(int behavior) {
switch (behavior) {
case SHORT_PRESS_WINDOW_NOTHING:
return "SHORT_PRESS_WINDOW_NOTHING";
case SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE:
return "SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE";
default:
return Integer.toString(behavior);
}
}
private static String shortPressOnStemPrimaryBehaviorToString(int behavior) {
switch (behavior) {
case SHORT_PRESS_PRIMARY_NOTHING:
return "SHORT_PRESS_PRIMARY_NOTHING";
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
return "SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS";
case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
return "SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY";
default:
return Integer.toString(behavior);
}
}
private static String doublePressOnStemPrimaryBehaviorToString(int behavior) {
switch (behavior) {
case DOUBLE_PRESS_PRIMARY_NOTHING:
return "DOUBLE_PRESS_PRIMARY_NOTHING";
case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
return "DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP";
default:
return Integer.toString(behavior);
}
}
private static String triplePressOnStemPrimaryBehaviorToString(int behavior) {
switch (behavior) {
case TRIPLE_PRESS_PRIMARY_NOTHING:
return "TRIPLE_PRESS_PRIMARY_NOTHING";
case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
return "TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY";
default:
return Integer.toString(behavior);
}
}
private static String longPressOnStemPrimaryBehaviorToString(int behavior) {
switch (behavior) {
case LONG_PRESS_PRIMARY_NOTHING:
return "LONG_PRESS_PRIMARY_NOTHING";
case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
return "LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT";
default:
return Integer.toString(behavior);
}
}
private static String lidBehaviorToString(int behavior) {
switch (behavior) {
case LID_BEHAVIOR_LOCK:
return "LID_BEHAVIOR_LOCK";
case LID_BEHAVIOR_SLEEP:
return "LID_BEHAVIOR_SLEEP";
case LID_BEHAVIOR_NONE:
return "LID_BEHAVIOR_NONE";
default:
return Integer.toString(behavior);
}
}
public static boolean isLongPressToAssistantEnabled(Context context) {
ContentResolver resolver = context.getContentResolver();
int longPressToAssistant = Settings.System.getIntForUser(resolver,
Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
/* def= */ 1,
UserHandle.USER_CURRENT);
if(Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "longPressToAssistant = " + longPressToAssistant);
}
return (longPressToAssistant == 1);
}
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
private static final String DP_EXIST = "DP=1";
private static final String NAME = "hdmi";
private boolean init(ExtconInfo hdmi) {
boolean plugged = false;
try {
plugged = parseStateFromFile(hdmi);
} catch (FileNotFoundException e) {
Slog.w(TAG,
hdmi.getStatePath()
+ " not found while attempting to determine initial state",
e);
} catch (IOException e) {
Slog.e(TAG,
"Error reading " + hdmi.getStatePath()
+ " while attempting to determine initial state",
e);
}
startObserving(hdmi);
return plugged;
}
@Override
public void updateState(ExtconInfo extconInfo, String eventName, Boolean state) {
mDefaultDisplayPolicy.setHdmiPlugged(state);
}
@Override
public Boolean parseState(ExtconInfo extconIfno, String state) {
// extcon event state changes from kernel4.9
// new state will be like STATE=HDMI=1
// or like STATE=DP=1 for newer kernel
return state.contains(HDMI_EXIST) || state.contains(DP_EXIST);
}
}
private void launchTargetSearchActivity() {
Intent intent;
if (mSearchKeyTargetActivity != null) {
intent = new Intent();
intent.setComponent(mSearchKeyTargetActivity);
} else {
intent = new Intent(Intent.ACTION_WEB_SEARCH);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
try {
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
} catch (ActivityNotFoundException ignore) {
Slog.e(TAG, "Could not resolve activity with : "
+ intent.getComponent().flattenToString()
+ " name.");
}
}
/** A helper class to check button override permission. */
static class ButtonOverridePermissionChecker {
boolean canAppOverrideSystemKey(Context context, int uid) {
return PermissionChecker.checkPermissionForDataDelivery(
context,
OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW,
PID_UNKNOWN,
uid,
null,
null,
null)
== PERMISSION_GRANTED;
}
}
}