| /* |
| * 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.SYSTEM_ALERT_WINDOW; |
| import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; |
| import static android.app.AppOpsManager.OP_TOAST_WINDOW; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; |
| import static android.content.Context.CONTEXT_RESTRICTED; |
| import static android.content.Context.DISPLAY_SERVICE; |
| import static android.content.Context.WINDOW_SERVICE; |
| 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.content.res.Configuration.EMPTY; |
| import static android.content.res.Configuration.UI_MODE_TYPE_CAR; |
| import static android.content.res.Configuration.UI_MODE_TYPE_MASK; |
| import static android.os.Build.VERSION_CODES.M; |
| import static android.os.Build.VERSION_CODES.O; |
| import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| import static android.view.Display.STATE_OFF; |
| import static android.view.WindowManager.DOCKED_LEFT; |
| import static android.view.WindowManager.DOCKED_RIGHT; |
| import static android.view.WindowManager.DOCKED_TOP; |
| import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; |
| 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.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; |
| import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; |
| import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; |
| import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; |
| import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; |
| import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; |
| import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; |
| import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; |
| 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.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; |
| import static android.view.WindowManager.LayoutParams.MATCH_PARENT; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR; |
| import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; |
| import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; |
| import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; |
| import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; |
| import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; |
| import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; |
| import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; |
| 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_APPLICATION_STARTING; |
| import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; |
| import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; |
| import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; |
| import static android.view.WindowManager.LayoutParams.TYPE_DREAM; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; |
| 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_MAGNIFICATION_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; |
| import static android.view.WindowManager.LayoutParams.TYPE_PHONE; |
| import static android.view.WindowManager.LayoutParams.TYPE_POINTER; |
| import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; |
| import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; |
| 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_SCREENSHOT; |
| import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_TOAST; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; |
| import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; |
| import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; |
| import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; |
| import static android.view.WindowManagerGlobal.ADD_OKAY; |
| import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; |
| |
| 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_ABSENT; |
| 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.FOCUSED_APP_TOKEN; |
| import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_WINDOW; |
| import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR; |
| import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD; |
| 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.LAST_SYSTEM_UI_FLAGS; |
| import static com.android.server.wm.WindowManagerPolicyProto.NAVIGATION_BAR; |
| import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION; |
| import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION_LISTENER; |
| 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.STATUS_BAR; |
| import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW; |
| import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW; |
| import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE; |
| |
| import android.annotation.Nullable; |
| import android.app.ActivityManager; |
| import android.app.ActivityManagerInternal; |
| import android.app.ActivityManagerInternal.SleepToken; |
| import android.app.ActivityThread; |
| import android.app.AppOpsManager; |
| import android.app.IUiModeManager; |
| import android.app.ProgressDialog; |
| import android.app.SearchManager; |
| import android.app.StatusBarManager; |
| import android.app.UiModeManager; |
| import android.content.ActivityNotFoundException; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.res.CompatibilityInfo; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.content.res.TypedArray; |
| import android.database.ContentObserver; |
| import android.graphics.PixelFormat; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.hardware.display.DisplayManager; |
| import android.hardware.hdmi.HdmiControlManager; |
| import android.hardware.hdmi.HdmiPlaybackClient; |
| import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; |
| import android.hardware.input.InputManager; |
| import android.hardware.input.InputManagerInternal; |
| import android.hardware.power.V1_0.PowerHint; |
| import android.media.AudioAttributes; |
| 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.FactoryTest; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IDeviceIdleController; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.PowerManager; |
| 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.UEventObserver; |
| import android.os.UserHandle; |
| import android.os.VibrationEffect; |
| import android.os.Vibrator; |
| import android.provider.MediaStore; |
| import android.provider.Settings; |
| 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.ArraySet; |
| import android.util.DisplayMetrics; |
| import android.util.EventLog; |
| import android.util.Log; |
| import android.util.LongSparseArray; |
| 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.DisplayCutout; |
| import android.view.Gravity; |
| import android.view.HapticFeedbackConstants; |
| import android.view.IApplicationToken; |
| import android.view.IWindowManager; |
| import android.view.InputChannel; |
| import android.view.InputDevice; |
| import android.view.InputEvent; |
| import android.view.InputEventReceiver; |
| import android.view.KeyCharacterMap; |
| import android.view.KeyCharacterMap.FallbackAction; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.PointerIcon; |
| import android.view.Surface; |
| import android.view.View; |
| import android.view.ViewConfiguration; |
| import android.view.WindowManager; |
| import android.view.WindowManager.LayoutParams; |
| import android.view.WindowManagerGlobal; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityManager; |
| import android.view.animation.Animation; |
| import android.view.animation.AnimationSet; |
| import android.view.animation.AnimationUtils; |
| import android.view.autofill.AutofillManagerInternal; |
| import android.view.inputmethod.InputMethodManagerInternal; |
| |
| import com.android.internal.R; |
| import com.android.internal.accessibility.AccessibilityShortcutController; |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.logging.MetricsLogger; |
| import com.android.internal.logging.nano.MetricsProto; |
| import com.android.internal.policy.IKeyguardDismissCallback; |
| import com.android.internal.policy.IShortcutService; |
| import com.android.internal.policy.KeyguardDismissCallback; |
| import com.android.internal.policy.PhoneWindow; |
| import com.android.internal.statusbar.IStatusBarService; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.internal.util.ScreenshotHelper; |
| import com.android.internal.util.ScreenShapeHelper; |
| import com.android.internal.widget.PointerLocationView; |
| import com.android.server.GestureLauncherService; |
| import com.android.server.LocalServices; |
| import com.android.server.SystemServiceManager; |
| 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.vr.VrManagerInternal; |
| import com.android.server.wm.AppTransition; |
| import com.android.server.wm.DisplayFrames; |
| import com.android.server.wm.WindowManagerInternal; |
| import com.android.server.wm.WindowManagerInternal.AppTransitionListener; |
| |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.List; |
| |
| /** |
| * 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 DEBUG = false; |
| static final boolean localLOGV = false; |
| static final boolean DEBUG_INPUT = false; |
| static final boolean DEBUG_KEYGUARD = false; |
| static final boolean DEBUG_LAYOUT = false; |
| static final boolean DEBUG_SPLASH_SCREEN = false; |
| static final boolean DEBUG_WAKEUP = false; |
| static final boolean SHOW_SPLASH_SCREENS = true; |
| |
| // 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; |
| |
| static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false; |
| |
| 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 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 VERY_LONG_PRESS_POWER_NOTHING = 0; |
| static final int VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS = 1; |
| |
| 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 LONG_PRESS_BACK_NOTHING = 0; |
| static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1; |
| |
| // These need to match the documentation/constant in |
| // core/res/res/values/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 LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_ASSIST; |
| |
| static final int DOUBLE_TAP_HOME_NOTHING = 0; |
| static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1; |
| |
| 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; |
| |
| static final int PENDING_KEY_NULL = -1; |
| |
| // Controls navigation bar opacity depending on which workspace stacks are currently |
| // visible. |
| // Nav bar is always opaque when either the freeform stack or docked stack is visible. |
| static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0; |
| // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque. |
| static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 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"; |
| |
| /** |
| * These are the system UI flags that, when changing, can cause the layout |
| * of the screen to change. |
| */ |
| static final int SYSTEM_UI_CHANGING_LAYOUT = |
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.STATUS_BAR_TRANSLUCENT |
| | View.NAVIGATION_BAR_TRANSLUCENT |
| | View.STATUS_BAR_TRANSPARENT |
| | View.NAVIGATION_BAR_TRANSPARENT; |
| |
| private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() |
| .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) |
| .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) |
| .build(); |
| |
| // The panic gesture may become active only after the keyguard is dismissed and the immersive |
| // app shows again. If that doesn't happen for 30s we drop the gesture. |
| private static final long PANIC_GESTURE_EXPIRATION = 30000; |
| |
| private static final String SYSUI_PACKAGE = "com.android.systemui"; |
| private static final String SYSUI_SCREENSHOT_SERVICE = |
| "com.android.systemui.screenshot.TakeScreenshotService"; |
| private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER = |
| "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver"; |
| |
| /** |
| * Keyguard stuff |
| */ |
| private boolean mKeyguardDrawnOnce; |
| |
| /* Table of Application Launch keys. Maps from key codes to intent categories. |
| * |
| * These are special keys that are used to launch particular kinds of applications, |
| * such as a web browser. HID defines nearly a hundred of them in the Consumer (0x0C) |
| * usage page. We don't support quite that many yet... |
| */ |
| static SparseArray<String> sApplicationLaunchKeyCategories; |
| static { |
| sApplicationLaunchKeyCategories = new SparseArray<String>(); |
| sApplicationLaunchKeyCategories.append( |
| KeyEvent.KEYCODE_EXPLORER, Intent.CATEGORY_APP_BROWSER); |
| sApplicationLaunchKeyCategories.append( |
| KeyEvent.KEYCODE_ENVELOPE, Intent.CATEGORY_APP_EMAIL); |
| sApplicationLaunchKeyCategories.append( |
| KeyEvent.KEYCODE_CONTACTS, Intent.CATEGORY_APP_CONTACTS); |
| sApplicationLaunchKeyCategories.append( |
| KeyEvent.KEYCODE_CALENDAR, Intent.CATEGORY_APP_CALENDAR); |
| sApplicationLaunchKeyCategories.append( |
| KeyEvent.KEYCODE_MUSIC, Intent.CATEGORY_APP_MUSIC); |
| sApplicationLaunchKeyCategories.append( |
| KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR); |
| } |
| |
| private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200; |
| |
| /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */ |
| static final int WAITING_FOR_DRAWN_TIMEOUT = 1000; |
| |
| /** Amount of time (in milliseconds) a toast window can be shown. */ |
| public static final int TOAST_WINDOW_TIMEOUT = 3500; // 3.5 seconds |
| |
| /** |
| * 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(); |
| |
| Context mContext; |
| IWindowManager mWindowManager; |
| WindowManagerFuncs mWindowManagerFuncs; |
| WindowManagerInternal mWindowManagerInternal; |
| PowerManager mPowerManager; |
| ActivityManagerInternal mActivityManagerInternal; |
| AutofillManagerInternal mAutofillManagerInternal; |
| InputManagerInternal mInputManagerInternal; |
| InputMethodManagerInternal mInputMethodManagerInternal; |
| DreamManagerInternal mDreamManagerInternal; |
| PowerManagerInternal mPowerManagerInternal; |
| IStatusBarService mStatusBarService; |
| StatusBarManagerInternal mStatusBarManagerInternal; |
| AudioManagerInternal mAudioManagerInternal; |
| boolean mPreloadedRecentApps; |
| final Object mServiceAquireLock = new Object(); |
| Vibrator mVibrator; // Vibrator for giving feedback of orientation changes |
| SearchManager mSearchManager; |
| AccessibilityManager mAccessibilityManager; |
| BurnInProtectionHelper mBurnInProtectionHelper; |
| AppOpsManager mAppOpsManager; |
| private ScreenshotHelper mScreenshotHelper; |
| private boolean mHasFeatureWatch; |
| private boolean mHasFeatureLeanback; |
| |
| // Assigned on main thread, accessed on UI thread |
| volatile VrManagerInternal mVrManagerInternal; |
| |
| // Vibrator pattern for haptic feedback of a long press. |
| long[] mLongPressVibePattern; |
| |
| // Vibrator pattern for a short vibration when tapping on a day/month/year date of a Calendar. |
| long[] mCalendarDateVibePattern; |
| |
| // Vibrator pattern for haptic feedback during boot when safe mode is enabled. |
| long[] mSafeModeEnabledVibePattern; |
| |
| /** 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; |
| |
| boolean mSafeMode; |
| private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); |
| WindowState mStatusBar = null; |
| private final int[] mStatusBarHeightForRotation = new int[4]; |
| WindowState mNavigationBar = null; |
| boolean mHasNavigationBar = false; |
| boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side? |
| @NavigationBarPosition |
| int mNavigationBarPosition = NAV_BAR_BOTTOM; |
| int[] mNavigationBarHeightForRotationDefault = new int[4]; |
| int[] mNavigationBarWidthForRotationDefault = new int[4]; |
| int[] mNavigationBarHeightForRotationInCarMode = new int[4]; |
| int[] mNavigationBarWidthForRotationInCarMode = new int[4]; |
| |
| private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>(); |
| |
| // 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; |
| KeyguardServiceDelegate mKeyguardDelegate; |
| private boolean mKeyguardBound; |
| final Runnable mWindowManagerDrawCallback = new Runnable() { |
| @Override |
| public void run() { |
| if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!"); |
| mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE); |
| } |
| }; |
| final DrawnListener mKeyguardDrawnCallback = new DrawnListener() { |
| @Override |
| public void onDrawn() { |
| if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn."); |
| mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE); |
| } |
| }; |
| |
| GlobalActions mGlobalActions; |
| Handler mHandler; |
| WindowState mLastInputMethodWindow = null; |
| WindowState mLastInputMethodTargetWindow = null; |
| |
| // 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 mBeganFromNonInteractive; |
| volatile int mPowerKeyPressCounter; |
| volatile boolean mEndCallKeyHandled; |
| volatile boolean mCameraGestureTriggeredDuringGoingToSleep; |
| volatile boolean mGoingToSleep; |
| volatile boolean mRequestedOrGoingToSleep; |
| volatile boolean mRecentsVisible; |
| volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true; |
| volatile boolean mPictureInPictureVisible; |
| // Written by vr manager thread, only read in this class. |
| volatile private boolean mPersistentVrModeEnabled; |
| 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; |
| boolean mLanguageSwitchKeyPressed; |
| |
| int mLidState = LID_ABSENT; |
| int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT; |
| boolean mHaveBuiltInKeyboard; |
| |
| boolean mSystemReady; |
| boolean mSystemBooted; |
| boolean mHdmiPlugged; |
| HdmiControl mHdmiControl; |
| IUiModeManager mUiModeManager; |
| int mUiMode; |
| int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED; |
| int mLidOpenRotation; |
| int mCarDockRotation; |
| int mDeskDockRotation; |
| int mUndockedHdmiRotation; |
| int mDemoHdmiRotation; |
| boolean mDemoHdmiRotationLock; |
| int mDemoRotation; |
| boolean mDemoRotationLock; |
| |
| boolean mWakeGestureEnabledSetting; |
| MyWakeGestureListener mWakeGestureListener; |
| |
| // Default display does not rotate, apps that require non-default orientation will have to |
| // have the orientation emulated. |
| private boolean mForceDefaultOrientation = false; |
| |
| int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; |
| int mUserRotation = Surface.ROTATION_0; |
| |
| boolean mSupportAutoRotation; |
| int mAllowAllRotations = -1; |
| boolean mCarDockEnablesAccelerometer; |
| boolean mDeskDockEnablesAccelerometer; |
| int mLidKeyboardAccessibility; |
| int mLidNavigationAccessibility; |
| boolean mLidControlsScreenLock; |
| boolean mLidControlsSleep; |
| int mShortPressOnPowerBehavior; |
| int mLongPressOnPowerBehavior; |
| int mVeryLongPressOnPowerBehavior; |
| int mDoublePressOnPowerBehavior; |
| int mTriplePressOnPowerBehavior; |
| int mLongPressOnBackBehavior; |
| int mShortPressOnSleepBehavior; |
| int mShortPressOnWindowBehavior; |
| volatile boolean mAwake; |
| boolean mScreenOnEarly; |
| boolean mScreenOnFully; |
| ScreenOnListener mScreenOnListener; |
| boolean mKeyguardDrawComplete; |
| boolean mWindowManagerDrawComplete; |
| boolean mOrientationSensorEnabled = false; |
| int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; |
| boolean mHasSoftInput = false; |
| boolean mTranslucentDecorEnabled = true; |
| boolean mUseTvRouting; |
| int mVeryLongPressTimeout; |
| boolean mAllowStartActivityForLongPressOnPowerDuringSetup; |
| MetricsLogger mLogger; |
| |
| private boolean mHandleVolumeKeysInWM; |
| |
| int mPointerLocationMode = 0; // guarded by mLock |
| |
| // The last window we were told about in focusChanged. |
| WindowState mFocusedWindow; |
| IApplicationToken mFocusedApp; |
| |
| PointerLocationView mPointerLocationView; |
| |
| // During layout, the layer at which the doc window is placed. |
| int mDockLayer; |
| // During layout, this is the layer of the status bar. |
| int mStatusBarLayer; |
| int mLastSystemUiFlags; |
| // Bits that we are in the process of clearing, so we want to prevent |
| // them from being set by applications until everything has been updated |
| // to have them clear. |
| int mResettingSystemUiFlags = 0; |
| // Bits that we are currently always keeping cleared. |
| int mForceClearedSystemUiFlags = 0; |
| int mLastFullscreenStackSysUiFlags; |
| int mLastDockedStackSysUiFlags; |
| final Rect mNonDockedStackBounds = new Rect(); |
| final Rect mDockedStackBounds = new Rect(); |
| final Rect mLastNonDockedStackBounds = new Rect(); |
| final Rect mLastDockedStackBounds = new Rect(); |
| |
| // What we last reported to system UI about whether the compatibility |
| // menu needs to be displayed. |
| boolean mLastFocusNeedsMenu = false; |
| // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. |
| private long mPendingPanicGestureUptime; |
| |
| InputConsumer mInputConsumer = null; |
| |
| static final Rect mTmpParentFrame = new Rect(); |
| static final Rect mTmpDisplayFrame = new Rect(); |
| static final Rect mTmpOverscanFrame = new Rect(); |
| static final Rect mTmpContentFrame = new Rect(); |
| static final Rect mTmpVisibleFrame = new Rect(); |
| static final Rect mTmpDecorFrame = new Rect(); |
| static final Rect mTmpStableFrame = new Rect(); |
| static final Rect mTmpNavigationFrame = new Rect(); |
| static final Rect mTmpOutsetFrame = new Rect(); |
| private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); |
| private static final Rect mTmpRect = new Rect(); |
| |
| WindowState mTopFullscreenOpaqueWindowState; |
| WindowState mTopFullscreenOpaqueOrDimmingWindowState; |
| WindowState mTopDockedOpaqueWindowState; |
| WindowState mTopDockedOpaqueOrDimmingWindowState; |
| boolean mTopIsFullscreen; |
| boolean mForceStatusBar; |
| boolean mForceStatusBarFromKeyguard; |
| private boolean mForceStatusBarTransparent; |
| int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED; |
| boolean mForcingShowNavBar; |
| int mForcingShowNavBarLayer; |
| |
| private boolean mPendingKeyguardOccluded; |
| private boolean mKeyguardOccludedChanged; |
| private boolean mNotifyUserActivity; |
| |
| boolean mShowingDream; |
| private boolean mLastShowingDream; |
| boolean mDreamingLockscreen; |
| boolean mDreamingSleepTokenNeeded; |
| private boolean mWindowSleepTokenNeeded; |
| private boolean mLastWindowSleepTokenNeeded; |
| |
| @GuardedBy("mHandler") |
| private SleepToken mWindowSleepToken; |
| |
| SleepToken mDreamingSleepToken; |
| SleepToken mScreenOffSleepToken; |
| volatile boolean mKeyguardOccluded; |
| boolean mHomePressed; |
| boolean mHomeConsumed; |
| boolean mHomeDoubleTapPending; |
| Intent mHomeIntent; |
| Intent mCarDockIntent; |
| Intent mDeskDockIntent; |
| Intent mVrHeadsetHomeIntent; |
| boolean mSearchKeyShortcutPending; |
| boolean mConsumeSearchKeyUp; |
| boolean mPendingMetaAction; |
| boolean mPendingCapsLockToggle; |
| int mMetaState; |
| int mInitialMetaState; |
| boolean mForceShowSystemBars; |
| |
| // support for activating the lock screen while the screen is on |
| boolean mAllowLockscreenWhenOn; |
| 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; |
| |
| // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION) |
| int mShowRotationSuggestions; |
| |
| // Whether system navigation keys are enabled |
| boolean mSystemNavigationKeysEnabled; |
| |
| Display mDisplay; |
| |
| int mLandscapeRotation = 0; // default landscape rotation |
| int mSeascapeRotation = 0; // "other" landscape rotation, 180 degrees from mLandscapeRotation |
| int mPortraitRotation = 0; // default portrait rotation |
| int mUpsideDownRotation = 0; // "other" portrait rotation |
| |
| // What we do when the user long presses on home |
| private int mLongPressOnHomeBehavior; |
| |
| // What we do when the user double-taps on home |
| private int mDoubleTapOnHomeBehavior; |
| |
| // 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; |
| |
| // Whether to support long press from power button in non-interactive mode |
| private boolean mSupportLongPressPowerWhenNonInteractive; |
| |
| // Whether to go to sleep entering theater mode from power button |
| private boolean mGoToSleepOnButtonPressTheaterMode; |
| |
| // Screenshot trigger states |
| // Time to volume and power must be pressed within this interval of each other. |
| private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150; |
| // Increase the chord delay when taking a screenshot from the keyguard |
| private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f; |
| private boolean mScreenshotChordEnabled; |
| private boolean mScreenshotChordVolumeDownKeyTriggered; |
| private long mScreenshotChordVolumeDownKeyTime; |
| private boolean mScreenshotChordVolumeDownKeyConsumed; |
| private boolean mA11yShortcutChordVolumeUpKeyTriggered; |
| private long mA11yShortcutChordVolumeUpKeyTime; |
| private boolean mA11yShortcutChordVolumeUpKeyConsumed; |
| |
| private boolean mScreenshotChordPowerKeyTriggered; |
| private long mScreenshotChordPowerKeyTime; |
| |
| // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up |
| private int mRingerToggleChord = VOLUME_HUSH_OFF; |
| |
| private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000; |
| |
| private boolean mBugreportTvKey1Pressed; |
| private boolean mBugreportTvKey2Pressed; |
| private boolean mBugreportTvScheduled; |
| |
| private boolean mAccessibilityTvKey1Pressed; |
| private boolean mAccessibilityTvKey2Pressed; |
| private boolean mAccessibilityTvScheduled; |
| |
| /* The number of steps between min and max brightness */ |
| private static final int BRIGHTNESS_STEPS = 10; |
| |
| SettingsObserver mSettingsObserver; |
| ShortcutManager mShortcutManager; |
| 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 LogDecelerateInterpolator mLogDecelerateInterpolator |
| = new LogDecelerateInterpolator(100, 0); |
| |
| private final MutableBoolean mTmpBoolean = new MutableBoolean(false); |
| |
| private boolean mAodShowing; |
| |
| private static final int MSG_ENABLE_POINTER_LOCATION = 1; |
| private static final int MSG_DISABLE_POINTER_LOCATION = 2; |
| 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_POWER_DELAYED_PRESS = 13; |
| private static final int MSG_POWER_LONG_PRESS = 14; |
| private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15; |
| private static final int MSG_REQUEST_TRANSIENT_BARS = 16; |
| private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 17; |
| private static final int MSG_BACK_LONG_PRESS = 18; |
| private static final int MSG_DISPOSE_INPUT_CONSUMER = 19; |
| private static final int MSG_ACCESSIBILITY_SHORTCUT = 20; |
| private static final int MSG_BUGREPORT_TV = 21; |
| private static final int MSG_ACCESSIBILITY_TV = 22; |
| private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 23; |
| private static final int MSG_SYSTEM_KEY_PRESS = 24; |
| private static final int MSG_HANDLE_ALL_APPS = 25; |
| private static final int MSG_LAUNCH_ASSIST = 26; |
| private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27; |
| private static final int MSG_POWER_VERY_LONG_PRESS = 28; |
| private static final int MSG_NOTIFY_USER_ACTIVITY = 29; |
| private static final int MSG_RINGER_TOGGLE_CHORD = 30; |
| |
| private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0; |
| private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1; |
| |
| private class PolicyHandler extends Handler { |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_ENABLE_POINTER_LOCATION: |
| enablePointerLocation(); |
| break; |
| case MSG_DISABLE_POINTER_LOCATION: |
| disablePointerLocation(); |
| break; |
| 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: |
| if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete"); |
| finishWindowsDrawn(); |
| break; |
| case MSG_HIDE_BOOT_MESSAGE: |
| handleHideBootMessage(); |
| break; |
| case MSG_LAUNCH_ASSIST: |
| final int deviceId = msg.arg1; |
| final String hint = (String) msg.obj; |
| launchAssistAction(hint, deviceId); |
| break; |
| case MSG_LAUNCH_ASSIST_LONG_PRESS: |
| launchAssistLongPressAction(); |
| break; |
| case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: |
| launchVoiceAssistWithWakeLock(); |
| break; |
| case MSG_POWER_DELAYED_PRESS: |
| powerPress((Long)msg.obj, msg.arg1 != 0, msg.arg2); |
| finishPowerKeyPress(); |
| break; |
| case MSG_POWER_LONG_PRESS: |
| powerLongPress(); |
| break; |
| case MSG_POWER_VERY_LONG_PRESS: |
| powerVeryLongPress(); |
| break; |
| case MSG_UPDATE_DREAMING_SLEEP_TOKEN: |
| updateDreamingSleepToken(msg.arg1 != 0); |
| break; |
| case MSG_REQUEST_TRANSIENT_BARS: |
| WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) ? |
| mStatusBar : mNavigationBar; |
| if (targetBar != null) { |
| requestTransientBars(targetBar); |
| } |
| break; |
| case MSG_SHOW_PICTURE_IN_PICTURE_MENU: |
| showPictureInPictureMenuInternal(); |
| break; |
| case MSG_BACK_LONG_PRESS: |
| backLongPress(); |
| break; |
| case MSG_DISPOSE_INPUT_CONSUMER: |
| disposeInputConsumer((InputConsumer) msg.obj); |
| break; |
| case MSG_ACCESSIBILITY_SHORTCUT: |
| accessibilityShortcutActivated(); |
| break; |
| case MSG_BUGREPORT_TV: |
| requestFullBugreport(); |
| 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: |
| sendSystemKeyToStatusBar(msg.arg1); |
| break; |
| case MSG_HANDLE_ALL_APPS: |
| launchAllAppsAction(); |
| break; |
| case MSG_NOTIFY_USER_ACTIVITY: |
| removeMessages(MSG_NOTIFY_USER_ACTIVITY); |
| Intent intent = new Intent(ACTION_USER_ACTIVITY_NOTIFICATION); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); |
| mContext.sendBroadcastAsUser(intent, UserHandle.ALL, |
| android.Manifest.permission.USER_ACTIVITY); |
| case MSG_RINGER_TOGGLE_CHORD: |
| handleRingerChordGesture(); |
| break; |
| } |
| } |
| } |
| |
| private UEventObserver mHDMIObserver = new UEventObserver() { |
| @Override |
| public void onUEvent(UEventObserver.UEvent event) { |
| 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.ACCELEROMETER_ROTATION), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.System.getUriFor( |
| Settings.System.USER_ROTATION), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.System.getUriFor( |
| Settings.System.SCREEN_OFF_TIMEOUT), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.System.getUriFor( |
| Settings.System.POINTER_LOCATION), 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.IMMERSIVE_MODE_CONFIRMATIONS), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.Secure.getUriFor( |
| Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.Secure.getUriFor( |
| Settings.Secure.VOLUME_HUSH_GESTURE), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.Global.getUriFor( |
| Settings.Global.POLICY_CONTROL), false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(Settings.Secure.getUriFor( |
| Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this, |
| UserHandle.USER_ALL); |
| updateSettings(); |
| } |
| |
| @Override public void onChange(boolean selfChange) { |
| updateSettings(); |
| updateRotation(false); |
| } |
| } |
| |
| class MyWakeGestureListener extends WakeGestureListener { |
| MyWakeGestureListener(Context context, Handler handler) { |
| super(context, handler); |
| } |
| |
| @Override |
| public void onWakeUp() { |
| synchronized (mLock) { |
| if (shouldEnableWakeGestureLp()) { |
| performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); |
| wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, |
| "android.policy:GESTURE"); |
| } |
| } |
| } |
| } |
| |
| class MyOrientationListener extends WindowOrientationListener { |
| |
| private SparseArray<Runnable> mRunnableCache; |
| |
| MyOrientationListener(Context context, Handler handler) { |
| super(context, handler); |
| mRunnableCache = new SparseArray<>(5); |
| } |
| |
| private class UpdateRunnable implements Runnable { |
| private final int mRotation; |
| UpdateRunnable(int rotation) { |
| mRotation = rotation; |
| } |
| |
| @Override |
| public void run() { |
| // send interaction hint to improve redraw performance |
| mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0); |
| if (isRotationChoicePossible(mCurrentAppOrientation)) { |
| final boolean isValid = isValidRotationChoice(mCurrentAppOrientation, |
| mRotation); |
| sendProposedRotationChangeToStatusBarInternal(mRotation, isValid); |
| } else { |
| updateRotation(false); |
| } |
| } |
| } |
| |
| @Override |
| public void onProposedRotationChanged(int rotation) { |
| if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation); |
| Runnable r = mRunnableCache.get(rotation, null); |
| if (r == null){ |
| r = new UpdateRunnable(rotation); |
| mRunnableCache.put(rotation, r); |
| } |
| mHandler.post(r); |
| } |
| } |
| MyOrientationListener mOrientationListener; |
| |
| final IPersistentVrStateCallbacks mPersistentVrModeListener = |
| new IPersistentVrStateCallbacks.Stub() { |
| @Override |
| public void onPersistentVrStateChanged(boolean enabled) { |
| mPersistentVrModeEnabled = enabled; |
| } |
| }; |
| |
| private final StatusBarController mStatusBarController = new StatusBarController(); |
| |
| private final BarController mNavigationBarController = new BarController("NavigationBar", |
| View.NAVIGATION_BAR_TRANSIENT, |
| View.NAVIGATION_BAR_UNHIDE, |
| View.NAVIGATION_BAR_TRANSLUCENT, |
| StatusBarManager.WINDOW_NAVIGATION_BAR, |
| FLAG_TRANSLUCENT_NAVIGATION, |
| View.NAVIGATION_BAR_TRANSPARENT); |
| |
| private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener = |
| new BarController.OnBarVisibilityChangedListener() { |
| @Override |
| public void onBarVisibilityChanged(boolean visible) { |
| mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible); |
| } |
| }; |
| |
| private final Runnable mAcquireSleepTokenRunnable = () -> { |
| if (mWindowSleepToken != null) { |
| return; |
| } |
| mWindowSleepToken = mActivityManagerInternal.acquireSleepToken("WindowSleepToken", |
| DEFAULT_DISPLAY); |
| }; |
| |
| private final Runnable mReleaseSleepTokenRunnable = () -> { |
| if (mWindowSleepToken == null) { |
| return; |
| } |
| mWindowSleepToken.release(); |
| mWindowSleepToken = null; |
| }; |
| |
| private ImmersiveModeConfirmation mImmersiveModeConfirmation; |
| |
| @VisibleForTesting |
| SystemGesturesPointerEventListener mSystemGestures; |
| |
| 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 (mServiceAquireLock) { |
| if (mStatusBarService == null) { |
| mStatusBarService = IStatusBarService.Stub.asInterface( |
| ServiceManager.getService("statusbar")); |
| } |
| return mStatusBarService; |
| } |
| } |
| |
| StatusBarManagerInternal getStatusBarManagerInternal() { |
| synchronized (mServiceAquireLock) { |
| if (mStatusBarManagerInternal == null) { |
| mStatusBarManagerInternal = |
| LocalServices.getService(StatusBarManagerInternal.class); |
| } |
| return mStatusBarManagerInternal; |
| } |
| } |
| |
| AudioManagerInternal getAudioManagerInternal() { |
| synchronized (mServiceAquireLock) { |
| if (mAudioManagerInternal == null) { |
| mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); |
| } |
| return mAudioManagerInternal; |
| } |
| } |
| |
| /* |
| * We always let the sensor be switched on by default except when |
| * the user has explicitly disabled sensor based rotation or when the |
| * screen is switched off. |
| */ |
| boolean needSensorRunningLp() { |
| if (mSupportAutoRotation) { |
| if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR |
| || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR |
| || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT |
| || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) { |
| // If the application has explicitly requested to follow the |
| // orientation, then we need to turn the sensor on. |
| return true; |
| } |
| } |
| if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR) || |
| (mDeskDockEnablesAccelerometer && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) { |
| // enable accelerometer if we are docked in a dock that enables accelerometer |
| // orientation management, |
| return true; |
| } |
| if (mUserRotationMode == USER_ROTATION_LOCKED) { |
| // If the setting for using the sensor by default is enabled, then |
| // we will always leave it on. Note that the user could go to |
| // a window that forces an orientation that does not use the |
| // sensor and in theory we could turn it off... however, when next |
| // turning it on we won't have a good value for the current |
| // orientation for a little bit, which can cause orientation |
| // changes to lag, so we'd like to keep it always on. (It will |
| // still be turned off when the screen is off.) |
| |
| // When locked we can provide rotation suggestions users can approve to change the |
| // current screen rotation. To do this the sensor needs to be running. |
| return mSupportAutoRotation && |
| mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED; |
| } |
| return mSupportAutoRotation; |
| } |
| |
| /* |
| * Various use cases for invoking this function |
| * screen turning off, should always disable listeners if already enabled |
| * screen turned on and current app has sensor based orientation, enable listeners |
| * if not already enabled |
| * screen turned on and current app does not have sensor orientation, disable listeners if |
| * already enabled |
| * screen turning on and current app has sensor based orientation, enable listeners if needed |
| * screen turning on and current app has nosensor based orientation, do nothing |
| */ |
| void updateOrientationListenerLp() { |
| if (!mOrientationListener.canDetectOrientation()) { |
| // If sensor is turned off or nonexistent for some reason |
| return; |
| } |
| // Could have been invoked due to screen turning on or off or |
| // change of the currently visible window's orientation. |
| if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly |
| + ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation |
| + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled |
| + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete |
| + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); |
| |
| boolean disable = true; |
| // Note: We postpone the rotating of the screen until the keyguard as well as the |
| // window manager have reported a draw complete or the keyguard is going away in dismiss |
| // mode. |
| if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) { |
| if (needSensorRunningLp()) { |
| disable = false; |
| //enable listener if not already enabled |
| if (!mOrientationSensorEnabled) { |
| // Don't clear the current sensor orientation if the keyguard is going away in |
| // dismiss mode. This allows window manager to use the last sensor reading to |
| // determine the orientation vs. falling back to the last known orientation if |
| // the sensor reading was cleared which can cause it to relaunch the app that |
| // will show in the wrong orientation first before correcting leading to app |
| // launch delays. |
| mOrientationListener.enable(true /* clearCurrentRotation */); |
| if(localLOGV) Slog.v(TAG, "Enabling listeners"); |
| mOrientationSensorEnabled = true; |
| } |
| } |
| } |
| //check if sensors need to be disabled |
| if (disable && mOrientationSensorEnabled) { |
| mOrientationListener.disable(); |
| if(localLOGV) Slog.v(TAG, "Disabling listeners"); |
| mOrientationSensorEnabled = false; |
| } |
| } |
| |
| private void interceptBackKeyDown() { |
| MetricsLogger.count(mContext, "key_back_down", 1); |
| // Reset back key state for long press |
| mBackKeyHandled = false; |
| |
| if (hasLongPressOnBackBehavior()) { |
| Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS); |
| msg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(msg, |
| ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); |
| } |
| } |
| |
| // returns true if the key was handled and should not be passed to the user |
| private boolean interceptBackKeyUp(KeyEvent event) { |
| // Cache handled state |
| boolean handled = mBackKeyHandled; |
| |
| // Reset back long press state |
| cancelPendingBackKeyAction(); |
| |
| 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 && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { |
| 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(); |
| } |
| |
| // Cancel multi-press detection timeout. |
| if (mPowerKeyPressCounter != 0) { |
| mHandler.removeMessages(MSG_POWER_DELAYED_PRESS); |
| } |
| |
| // Detect user pressing the power button in panic when an application has |
| // taken over the whole screen. |
| boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive, |
| SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags), |
| isNavBarEmpty(mLastSystemUiFlags)); |
| if (panic) { |
| mHandler.post(mHiddenNavPanic); |
| } |
| |
| // Abort possibly stuck animations. |
| mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); |
| |
| // Latch power key state to detect screenshot chord. |
| if (interactive && !mScreenshotChordPowerKeyTriggered |
| && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { |
| mScreenshotChordPowerKeyTriggered = true; |
| mScreenshotChordPowerKeyTime = event.getDownTime(); |
| interceptScreenshotChord(); |
| interceptRingerToggleChord(); |
| } |
| |
| // 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(); |
| } |
| } |
| |
| GestureLauncherService gestureService = LocalServices.getService( |
| GestureLauncherService.class); |
| boolean gesturedServiceIntercepted = false; |
| if (gestureService != null) { |
| gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, |
| mTmpBoolean); |
| if (mTmpBoolean.value && mRequestedOrGoingToSleep) { |
| mCameraGestureTriggeredDuringGoingToSleep = true; |
| } |
| } |
| |
| // Inform the StatusBar; but do not allow it to consume the event. |
| sendSystemKeyToStatusBarAsync(event.getKeyCode()); |
| |
| // 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 = hungUp || mScreenshotChordVolumeDownKeyTriggered |
| || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted; |
| if (!mPowerKeyHandled) { |
| if (interactive) { |
| // When interactive, we're already awake. |
| // Wait for a long press or for the button to be released to decide what to do. |
| if (hasLongPressOnPowerBehavior()) { |
| if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { |
| powerLongPress(); |
| } else { |
| Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS); |
| msg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(msg, |
| ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); |
| |
| if (hasVeryLongPressOnPowerBehavior()) { |
| Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); |
| longMsg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); |
| } |
| } |
| } |
| } else { |
| wakeUpFromPowerKey(event.getDownTime()); |
| |
| if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) { |
| if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { |
| powerLongPress(); |
| } else { |
| Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS); |
| msg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(msg, |
| ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); |
| |
| if (hasVeryLongPressOnPowerBehavior()) { |
| Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); |
| longMsg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); |
| } |
| } |
| |
| mBeganFromNonInteractive = true; |
| } else { |
| final int maxCount = getMaxMultiPressPowerCount(); |
| |
| if (maxCount <= 1) { |
| mPowerKeyHandled = true; |
| } else { |
| mBeganFromNonInteractive = true; |
| } |
| } |
| } |
| } |
| } |
| |
| private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) { |
| final boolean handled = canceled || mPowerKeyHandled; |
| mScreenshotChordPowerKeyTriggered = false; |
| cancelPendingScreenshotChordAction(); |
| cancelPendingPowerKeyAction(); |
| |
| if (!handled) { |
| // Figure out how to handle the key now that it has been released. |
| mPowerKeyPressCounter += 1; |
| |
| final int maxCount = getMaxMultiPressPowerCount(); |
| final long eventTime = event.getDownTime(); |
| if (mPowerKeyPressCounter < maxCount) { |
| // This could be a multi-press. Wait a little bit longer to confirm. |
| // Continue holding the wake lock. |
| Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS, |
| interactive ? 1 : 0, mPowerKeyPressCounter, eventTime); |
| msg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout()); |
| return; |
| } |
| |
| // No other actions. Handle it immediately. |
| powerPress(eventTime, interactive, mPowerKeyPressCounter); |
| } |
| |
| // Done. Reset our state. |
| finishPowerKeyPress(); |
| } |
| |
| private void finishPowerKeyPress() { |
| mBeganFromNonInteractive = false; |
| mPowerKeyPressCounter = 0; |
| if (mPowerKeyWakeLock.isHeld()) { |
| mPowerKeyWakeLock.release(); |
| } |
| } |
| |
| private void cancelPendingPowerKeyAction() { |
| if (!mPowerKeyHandled) { |
| mPowerKeyHandled = true; |
| mHandler.removeMessages(MSG_POWER_LONG_PRESS); |
| } |
| if (hasVeryLongPressOnPowerBehavior()) { |
| mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); |
| } |
| } |
| |
| private void cancelPendingBackKeyAction() { |
| if (!mBackKeyHandled) { |
| mBackKeyHandled = true; |
| mHandler.removeMessages(MSG_BACK_LONG_PRESS); |
| } |
| } |
| |
| private void powerPress(long eventTime, boolean interactive, int count) { |
| if (mScreenOnEarly && !mScreenOnFully) { |
| Slog.i(TAG, "Suppressed redundant power key press while " |
| + "already in the process of turning the screen on."); |
| return; |
| } |
| Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive |
| + " count=" + count + " beganFromNonInteractive=" + mBeganFromNonInteractive + |
| " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior); |
| |
| if (count == 2) { |
| powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior); |
| } else if (count == 3) { |
| powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior); |
| } else if (interactive && !mBeganFromNonInteractive) { |
| switch (mShortPressOnPowerBehavior) { |
| case SHORT_PRESS_POWER_NOTHING: |
| break; |
| case SHORT_PRESS_POWER_GO_TO_SLEEP: |
| goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); |
| break; |
| case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: |
| goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, |
| PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); |
| break; |
| case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: |
| goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, |
| PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); |
| launchHomeFromHotKey(); |
| break; |
| case SHORT_PRESS_POWER_GO_HOME: |
| shortPressPowerGoHome(); |
| break; |
| case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: { |
| if (mDismissImeOnBackKeyPressed) { |
| if (mInputMethodManagerInternal == null) { |
| mInputMethodManagerInternal = |
| LocalServices.getService(InputMethodManagerInternal.class); |
| } |
| if (mInputMethodManagerInternal != null) { |
| mInputMethodManagerInternal.hideCurrentInputMethod(); |
| } |
| } else { |
| shortPressPowerGoHome(); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| private void goToSleep(long eventTime, int reason, int flags) { |
| mRequestedOrGoingToSleep = true; |
| mPowerManager.goToSleep(eventTime, reason, flags); |
| } |
| |
| private void shortPressPowerGoHome() { |
| launchHomeFromHotKey(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) { |
| goToSleep(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; |
| } |
| } |
| |
| private int getMaxMultiPressPowerCount() { |
| if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) { |
| return 3; |
| } |
| if (mDoublePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) { |
| return 2; |
| } |
| return 1; |
| } |
| |
| private void powerLongPress() { |
| final int behavior = getResolvedLongPressOnPowerBehavior(); |
| switch (behavior) { |
| case LONG_PRESS_POWER_NOTHING: |
| break; |
| case LONG_PRESS_POWER_GLOBAL_ACTIONS: |
| mPowerKeyHandled = true; |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| showGlobalActionsInternal(); |
| break; |
| case LONG_PRESS_POWER_SHUT_OFF: |
| case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: |
| mPowerKeyHandled = true; |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| 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; |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| final boolean keyguardActive = mKeyguardDelegate == null |
| ? false |
| : mKeyguardDelegate.isShowing(); |
| if (!keyguardActive) { |
| Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST); |
| if (mAllowStartActivityForLongPressOnPowerDuringSetup) { |
| mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); |
| } else { |
| startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); |
| } |
| } |
| break; |
| } |
| } |
| |
| private void powerVeryLongPress() { |
| switch (mVeryLongPressOnPowerBehavior) { |
| case VERY_LONG_PRESS_POWER_NOTHING: |
| break; |
| case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS: |
| mPowerKeyHandled = true; |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| showGlobalActionsInternal(); |
| break; |
| } |
| } |
| |
| private void backLongPress() { |
| mBackKeyHandled = true; |
| |
| switch (mLongPressOnBackBehavior) { |
| case LONG_PRESS_BACK_NOTHING: |
| break; |
| case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST: |
| final boolean keyguardActive = mKeyguardDelegate == null |
| ? false |
| : mKeyguardDelegate.isShowing(); |
| if (!keyguardActive) { |
| Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST); |
| startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); |
| } |
| break; |
| } |
| } |
| |
| private void accessibilityShortcutActivated() { |
| mAccessibilityShortcutController.performAccessibilityShortcut(); |
| } |
| |
| private void disposeInputConsumer(InputConsumer inputConsumer) { |
| if (inputConsumer != null) { |
| inputConsumer.dismiss(); |
| } |
| } |
| |
| private void sleepPress() { |
| if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) { |
| launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/); |
| } |
| } |
| |
| private void sleepRelease(long eventTime) { |
| 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)"); |
| goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0); |
| break; |
| } |
| } |
| |
| private int getResolvedLongPressOnPowerBehavior() { |
| if (FactoryTest.isLongPressOnPowerOffEnabled()) { |
| return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; |
| } |
| return mLongPressOnPowerBehavior; |
| } |
| |
| 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 void interceptScreenshotChord() { |
| if (mScreenshotChordEnabled |
| && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered |
| && !mA11yShortcutChordVolumeUpKeyTriggered) { |
| final long now = SystemClock.uptimeMillis(); |
| if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS |
| && now <= mScreenshotChordPowerKeyTime |
| + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { |
| mScreenshotChordVolumeDownKeyConsumed = true; |
| cancelPendingPowerKeyAction(); |
| mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); |
| mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay()); |
| } |
| } |
| } |
| |
| private void interceptAccessibilityShortcutChord() { |
| if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked()) |
| && mScreenshotChordVolumeDownKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered |
| && !mScreenshotChordPowerKeyTriggered) { |
| final long now = SystemClock.uptimeMillis(); |
| if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS |
| && now <= mA11yShortcutChordVolumeUpKeyTime |
| + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { |
| mScreenshotChordVolumeDownKeyConsumed = true; |
| mA11yShortcutChordVolumeUpKeyConsumed = true; |
| mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT), |
| getAccessibilityShortcutTimeout()); |
| } |
| } |
| } |
| |
| private void interceptRingerToggleChord() { |
| if (mRingerToggleChord != Settings.Secure.VOLUME_HUSH_OFF |
| && mScreenshotChordPowerKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered) { |
| final long now = SystemClock.uptimeMillis(); |
| if (now <= mA11yShortcutChordVolumeUpKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS |
| && now <= mScreenshotChordPowerKeyTime |
| + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { |
| mA11yShortcutChordVolumeUpKeyConsumed = true; |
| cancelPendingPowerKeyAction(); |
| |
| mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD), |
| getRingerToggleChordDelay()); |
| } |
| } |
| } |
| |
| private long getAccessibilityShortcutTimeout() { |
| ViewConfiguration config = ViewConfiguration.get(mContext); |
| return Settings.Secure.getIntForUser(mContext.getContentResolver(), |
| Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, mCurrentUserId) == 0 |
| ? config.getAccessibilityShortcutKeyTimeout() |
| : config.getAccessibilityShortcutKeyTimeoutAfterConfirmation(); |
| } |
| |
| private long getScreenshotChordLongPressDelay() { |
| if (mKeyguardDelegate.isShowing()) { |
| // Double the time it takes to take a screenshot from the keyguard |
| return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * |
| ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); |
| } |
| return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout(); |
| } |
| |
| private long getRingerToggleChordDelay() { |
| // Always timeout like a tap |
| return ViewConfiguration.getTapTimeout(); |
| } |
| |
| private void cancelPendingScreenshotChordAction() { |
| mHandler.removeCallbacks(mScreenshotRunnable); |
| } |
| |
| 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; |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| showGlobalActionsInternal(); |
| } |
| }; |
| |
| private class ScreenshotRunnable implements Runnable { |
| private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN; |
| |
| public void setScreenshotType(int screenshotType) { |
| mScreenshotType = screenshotType; |
| } |
| |
| @Override |
| public void run() { |
| mScreenshotHelper.takeScreenshot(mScreenshotType, |
| mStatusBar != null && mStatusBar.isVisibleLw(), |
| mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler); |
| } |
| } |
| |
| private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable(); |
| |
| @Override |
| public void showGlobalActions() { |
| mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS); |
| mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS); |
| } |
| |
| void showGlobalActionsInternal() { |
| if (mGlobalActions == null) { |
| mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); |
| } |
| final boolean keyguardShowing = isKeyguardShowingAndNotOccluded(); |
| mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); |
| if (keyguardShowing) { |
| // 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); |
| } |
| } |
| |
| boolean isDeviceProvisioned() { |
| return Settings.Global.getInt( |
| mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; |
| } |
| |
| boolean isUserSetupComplete() { |
| boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(), |
| Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; |
| if (mHasFeatureLeanback) { |
| isSetupComplete &= isTvUserSetupComplete(); |
| } |
| return isSetupComplete; |
| } |
| |
| private boolean isTvUserSetupComplete() { |
| return Settings.Secure.getIntForUser(mContext.getContentResolver(), |
| Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; |
| } |
| |
| private void handleShortPressOnHome() { |
| // 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. |
| if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) { |
| mDreamManagerInternal.stopDream(false /*immediate*/); |
| return; |
| } |
| |
| // Go home! |
| launchHomeFromHotKey(); |
| } |
| |
| /** |
| * 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 (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) { |
| 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 handleLongPressOnHome(int deviceId) { |
| if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) { |
| return; |
| } |
| mHomeConsumed = true; |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| switch (mLongPressOnHomeBehavior) { |
| case LONG_PRESS_HOME_ALL_APPS: |
| launchAllAppsAction(); |
| break; |
| case LONG_PRESS_HOME_ASSIST: |
| launchAssistAction(null, deviceId); |
| break; |
| default: |
| Log.w(TAG, "Undefined home long press behavior: " + mLongPressOnHomeBehavior); |
| break; |
| } |
| } |
| |
| private void launchAllAppsAction() { |
| Intent intent = new Intent(Intent.ACTION_ALL_APPS); |
| if (mHasFeatureLeanback) { |
| final PackageManager pm = mContext.getPackageManager(); |
| Intent intentLauncher = new Intent(Intent.ACTION_MAIN); |
| intentLauncher.addCategory(Intent.CATEGORY_HOME); |
| ResolveInfo resolveInfo = pm.resolveActivityAsUser(intentLauncher, |
| PackageManager.MATCH_SYSTEM_ONLY, |
| mCurrentUserId); |
| if (resolveInfo != null) { |
| intent.setPackage(resolveInfo.activityInfo.packageName); |
| } |
| } |
| startActivityAsUser(intent, UserHandle.CURRENT); |
| } |
| |
| private void handleDoubleTapOnHome() { |
| if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) { |
| mHomeConsumed = true; |
| toggleRecentApps(); |
| } |
| } |
| |
| 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(); |
| } |
| } |
| |
| private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() { |
| @Override |
| public void run() { |
| if (mHomeDoubleTapPending) { |
| mHomeDoubleTapPending = false; |
| handleShortPressOnHome(); |
| } |
| } |
| }; |
| |
| private boolean isRoundWindow() { |
| return mContext.getResources().getConfiguration().isScreenRound(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void init(Context context, IWindowManager windowManager, |
| WindowManagerFuncs windowManagerFuncs) { |
| mContext = context; |
| mWindowManager = windowManager; |
| mWindowManagerFuncs = windowManagerFuncs; |
| mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); |
| mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); |
| mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); |
| mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); |
| mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); |
| mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); |
| mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH); |
| mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); |
| mAccessibilityShortcutController = |
| new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId); |
| mLogger = new MetricsLogger(); |
| // Init display burn-in protection |
| boolean burnInProtectionEnabled = context.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 = context.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( |
| context, minHorizontal, maxHorizontal, minVertical, maxVertical, maxRadius); |
| } |
| |
| mHandler = new PolicyHandler(); |
| mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler); |
| mOrientationListener = new MyOrientationListener(mContext, mHandler); |
| try { |
| mOrientationListener.setCurrentRotation(windowManager.getDefaultDisplayRotation()); |
| } catch (RemoteException ex) { } |
| mSettingsObserver = new SettingsObserver(mHandler); |
| mSettingsObserver.observe(); |
| mShortcutManager = new ShortcutManager(context); |
| mUiMode = context.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 = context.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)context.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")); |
| mSupportAutoRotation = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_supportAutoRotation); |
| mLidOpenRotation = readRotation( |
| com.android.internal.R.integer.config_lidOpenRotation); |
| mCarDockRotation = readRotation( |
| com.android.internal.R.integer.config_carDockRotation); |
| mDeskDockRotation = readRotation( |
| com.android.internal.R.integer.config_deskDockRotation); |
| mUndockedHdmiRotation = readRotation( |
| com.android.internal.R.integer.config_undockedHdmiRotation); |
| mCarDockEnablesAccelerometer = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_carDockEnablesAccelerometer); |
| mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_deskDockEnablesAccelerometer); |
| mLidKeyboardAccessibility = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_lidKeyboardAccessibility); |
| mLidNavigationAccessibility = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_lidNavigationAccessibility); |
| mLidControlsScreenLock = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_lidControlsScreenLock); |
| mLidControlsSleep = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_lidControlsSleep); |
| mTranslucentDecorEnabled = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_enableTranslucentDecor); |
| |
| 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); |
| |
| mLongPressOnBackBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_longPressOnBackBehavior); |
| |
| mShortPressOnPowerBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_shortPressOnPowerBehavior); |
| mLongPressOnPowerBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_longPressOnPowerBehavior); |
| mVeryLongPressOnPowerBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_veryLongPressOnPowerBehavior); |
| mDoublePressOnPowerBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_doublePressOnPowerBehavior); |
| mTriplePressOnPowerBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_triplePressOnPowerBehavior); |
| mShortPressOnSleepBehavior = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_shortPressOnSleepBehavior); |
| mVeryLongPressTimeout = mContext.getResources().getInteger( |
| com.android.internal.R.integer.config_veryLongPressTimeout); |
| 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); |
| |
| readConfigurationDependentBehaviors(); |
| |
| mAccessibilityManager = (AccessibilityManager) context.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 = context.registerReceiver(mDockReceiver, filter); |
| if (intent != null) { |
| // Retrieve current sticky dock event broadcast. |
| mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, |
| Intent.EXTRA_DOCK_STATE_UNDOCKED); |
| } |
| |
| // register for dream-related broadcasts |
| filter = new IntentFilter(); |
| filter.addAction(Intent.ACTION_DREAMING_STARTED); |
| filter.addAction(Intent.ACTION_DREAMING_STOPPED); |
| context.registerReceiver(mDreamReceiver, filter); |
| |
| // register for multiuser-relevant broadcasts |
| filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); |
| context.registerReceiver(mMultiuserReceiver, filter); |
| |
| // monitor for system gestures |
| // TODO(multi-display): Needs to be display specific. |
| mSystemGestures = new SystemGesturesPointerEventListener(context, |
| new SystemGesturesPointerEventListener.Callbacks() { |
| @Override |
| public void onSwipeFromTop() { |
| if (mStatusBar != null) { |
| requestTransientBars(mStatusBar); |
| } |
| } |
| @Override |
| public void onSwipeFromBottom() { |
| if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| requestTransientBars(mNavigationBar); |
| } |
| } |
| @Override |
| public void onSwipeFromRight() { |
| if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) { |
| requestTransientBars(mNavigationBar); |
| } |
| } |
| @Override |
| public void onSwipeFromLeft() { |
| if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) { |
| requestTransientBars(mNavigationBar); |
| } |
| } |
| @Override |
| public void onFling(int duration) { |
| if (mPowerManagerInternal != null) { |
| mPowerManagerInternal.powerHint( |
| PowerHint.INTERACTION, duration); |
| } |
| } |
| @Override |
| public void onDebug() { |
| // no-op |
| } |
| @Override |
| public void onDown() { |
| mOrientationListener.onTouchStart(); |
| } |
| @Override |
| public void onUpOrCancel() { |
| mOrientationListener.onTouchEnd(); |
| } |
| @Override |
| public void onMouseHoverAtTop() { |
| mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); |
| Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); |
| msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS; |
| mHandler.sendMessageDelayed(msg, 500); |
| } |
| @Override |
| public void onMouseHoverAtBottom() { |
| mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); |
| Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); |
| msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION; |
| mHandler.sendMessageDelayed(msg, 500); |
| } |
| @Override |
| public void onMouseLeaveFromEdge() { |
| mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); |
| } |
| }); |
| mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext); |
| mWindowManagerFuncs.registerPointerEventListener(mSystemGestures); |
| |
| mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); |
| mLongPressVibePattern = getLongIntArray(mContext.getResources(), |
| com.android.internal.R.array.config_longPressVibePattern); |
| mCalendarDateVibePattern = getLongIntArray(mContext.getResources(), |
| com.android.internal.R.array.config_calendarDateVibePattern); |
| mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), |
| com.android.internal.R.array.config_safeModeEnabledVibePattern); |
| |
| mScreenshotChordEnabled = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_enableScreenshotChord); |
| |
| mGlobalKeyManager = new GlobalKeyManager(mContext); |
| |
| // Controls rotation and the like. |
| initializeHdmiState(); |
| |
| // Match current screen state. |
| if (!mPowerManager.isInteractive()) { |
| startedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER); |
| finishedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER); |
| } |
| |
| mWindowManagerInternal.registerAppTransitionListener( |
| mStatusBarController.getAppTransitionListener()); |
| mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() { |
| @Override |
| public int onAppTransitionStartingLocked(int transit, IBinder openToken, |
| IBinder closeToken, long duration, long statusBarAnimationStartTime, |
| long statusBarAnimationDuration) { |
| return handleStartTransitionForKeyguardLw(transit, duration); |
| } |
| |
| @Override |
| public void onAppTransitionCancelledLocked(int transit) { |
| handleStartTransitionForKeyguardLw(transit, 0 /* duration */); |
| } |
| }); |
| mKeyguardDelegate = new KeyguardServiceDelegate(mContext, |
| new StateCallback() { |
| @Override |
| public void onTrustedChanged() { |
| mWindowManagerFuncs.notifyKeyguardTrustedChanged(); |
| } |
| |
| @Override |
| public void onShowingChanged() { |
| mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); |
| } |
| }); |
| mScreenshotHelper = new ScreenshotHelper(mContext); |
| } |
| |
| /** |
| * 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_RECENT_SYSTEM_UI) { |
| mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING; |
| } |
| |
| mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING; |
| if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { |
| mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; |
| } |
| |
| mNavBarOpacityMode = res.getInteger( |
| com.android.internal.R.integer.config_navBarOpacityMode); |
| } |
| |
| @Override |
| public void setInitialDisplaySize(Display display, int width, int height, int density) { |
| // This method might be called before the policy has been fully initialized |
| // or for other displays we don't care about. |
| // TODO(multi-display): Define policy for secondary displays. |
| if (mContext == null || display.getDisplayId() != DEFAULT_DISPLAY) { |
| return; |
| } |
| mDisplay = display; |
| |
| final Resources res = mContext.getResources(); |
| int shortSize, longSize; |
| if (width > height) { |
| shortSize = height; |
| longSize = width; |
| mLandscapeRotation = Surface.ROTATION_0; |
| mSeascapeRotation = Surface.ROTATION_180; |
| if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) { |
| mPortraitRotation = Surface.ROTATION_90; |
| mUpsideDownRotation = Surface.ROTATION_270; |
| } else { |
| mPortraitRotation = Surface.ROTATION_270; |
| mUpsideDownRotation = Surface.ROTATION_90; |
| } |
| } else { |
| shortSize = width; |
| longSize = height; |
| mPortraitRotation = Surface.ROTATION_0; |
| mUpsideDownRotation = Surface.ROTATION_180; |
| if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) { |
| mLandscapeRotation = Surface.ROTATION_270; |
| mSeascapeRotation = Surface.ROTATION_90; |
| } else { |
| mLandscapeRotation = Surface.ROTATION_90; |
| mSeascapeRotation = Surface.ROTATION_270; |
| } |
| } |
| |
| // SystemUI (status bar) layout policy |
| int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density; |
| int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density; |
| |
| // Allow the navigation bar to move on non-square small devices (phones). |
| mNavigationBarCanMove = width != height && shortSizeDp < 600; |
| |
| mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); |
| |
| // Allow a system property to override this. Used by the emulator. |
| // See also hasNavigationBar(). |
| String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); |
| if ("1".equals(navBarOverride)) { |
| mHasNavigationBar = false; |
| } else if ("0".equals(navBarOverride)) { |
| mHasNavigationBar = true; |
| } |
| |
| // For demo purposes, allow the rotation of the HDMI display to be controlled. |
| // By default, HDMI locks rotation to landscape. |
| if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { |
| mDemoHdmiRotation = mPortraitRotation; |
| } else { |
| mDemoHdmiRotation = mLandscapeRotation; |
| } |
| mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false); |
| |
| // For demo purposes, allow the rotation of the remote display to be controlled. |
| // By default, remote display locks rotation to landscape. |
| if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) { |
| mDemoRotation = mPortraitRotation; |
| } else { |
| mDemoRotation = mLandscapeRotation; |
| } |
| mDemoRotationLock = SystemProperties.getBoolean( |
| "persist.demo.rotationlock", false); |
| |
| // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per |
| // http://developer.android.com/guide/practices/screens_support.html#range |
| // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen |
| // so if the orientation is forced, we need to respect that no matter what. |
| final boolean isCar = mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_AUTOMOTIVE); |
| // For TV, it's usually 960dp x 540dp, ignore the size limitation. |
| // so if the orientation is forced, we need to respect that no matter what. |
| final boolean isTv = mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_LEANBACK); |
| mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) && |
| res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) && |
| // For debug purposes the next line turns this feature off with: |
| // $ adb shell setprop config.override_forced_orient true |
| // $ adb shell wm size reset |
| !"true".equals(SystemProperties.get("config.override_forced_orient")); |
| } |
| |
| /** |
| * @return whether the navigation bar can be hidden, e.g. the device has a |
| * navigation bar and touch exploration is not enabled |
| */ |
| private boolean canHideNavigationBar() { |
| return mHasNavigationBar; |
| } |
| |
| @Override |
| public boolean isDefaultOrientationForced() { |
| return mForceDefaultOrientation; |
| } |
| |
| public void updateSettings() { |
| 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); |
| if (!mContext.getResources() |
| .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) { |
| mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF; |
| } |
| // Configure rotation suggestions. |
| int showRotationSuggestions = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.SHOW_ROTATION_SUGGESTIONS, |
| Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT, |
| UserHandle.USER_CURRENT); |
| if (mShowRotationSuggestions != showRotationSuggestions) { |
| mShowRotationSuggestions = showRotationSuggestions; |
| updateOrientationListenerLp(); // Enable, disable the orientation listener |
| } |
| |
| // 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(); |
| } |
| |
| // Configure rotation lock. |
| int userRotation = Settings.System.getIntForUser(resolver, |
| Settings.System.USER_ROTATION, Surface.ROTATION_0, |
| UserHandle.USER_CURRENT); |
| if (mUserRotation != userRotation) { |
| mUserRotation = userRotation; |
| updateRotation = true; |
| } |
| int userRotationMode = Settings.System.getIntForUser(resolver, |
| Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ? |
| WindowManagerPolicy.USER_ROTATION_FREE : |
| WindowManagerPolicy.USER_ROTATION_LOCKED; |
| if (mUserRotationMode != userRotationMode) { |
| mUserRotationMode = userRotationMode; |
| updateRotation = true; |
| updateOrientationListenerLp(); |
| } |
| |
| if (mSystemReady) { |
| int pointerLocation = Settings.System.getIntForUser(resolver, |
| Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT); |
| if (mPointerLocationMode != pointerLocation) { |
| mPointerLocationMode = pointerLocation; |
| mHandler.sendEmptyMessage(pointerLocation != 0 ? |
| MSG_ENABLE_POINTER_LOCATION : MSG_DISABLE_POINTER_LOCATION); |
| } |
| } |
| // 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; |
| } |
| if (mImmersiveModeConfirmation != null) { |
| mImmersiveModeConfirmation.loadSetting(mCurrentUserId); |
| } |
| } |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| PolicyControl.reloadFromSetting(mContext); |
| } |
| if (updateRotation) { |
| updateRotation(true); |
| } |
| } |
| |
| private void updateWakeGestureListenerLp() { |
| if (shouldEnableWakeGestureLp()) { |
| mWakeGestureListener.requestWakeUpTrigger(); |
| } else { |
| mWakeGestureListener.cancelWakeUpTrigger(); |
| } |
| } |
| |
| private boolean shouldEnableWakeGestureLp() { |
| return mWakeGestureEnabledSetting && !mAwake |
| && (!mLidControlsSleep || mLidState != LID_CLOSED) |
| && mWakeGestureListener.isSupported(); |
| } |
| |
| private void enablePointerLocation() { |
| if (mPointerLocationView == null) { |
| mPointerLocationView = new PointerLocationView(mContext); |
| mPointerLocationView.setPrintCoords(false); |
| WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| WindowManager.LayoutParams.MATCH_PARENT, |
| WindowManager.LayoutParams.MATCH_PARENT); |
| lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; |
| lp.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN |
| | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
| | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; |
| lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| if (ActivityManager.isHighEndGfx()) { |
| lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; |
| lp.privateFlags |= |
| WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; |
| } |
| lp.format = PixelFormat.TRANSLUCENT; |
| lp.setTitle("PointerLocation"); |
| WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE); |
| lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; |
| wm.addView(mPointerLocationView, lp); |
| mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView); |
| } |
| } |
| |
| private void disablePointerLocation() { |
| if (mPointerLocationView != null) { |
| mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView); |
| WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE); |
| wm.removeView(mPointerLocationView); |
| mPointerLocationView = null; |
| } |
| } |
| |
| private int readRotation(int resID) { |
| try { |
| int rotation = mContext.getResources().getInteger(resID); |
| switch (rotation) { |
| case 0: |
| return Surface.ROTATION_0; |
| case 90: |
| return Surface.ROTATION_90; |
| case 180: |
| return Surface.ROTATION_180; |
| case 270: |
| return Surface.ROTATION_270; |
| } |
| } catch (Resources.NotFoundException e) { |
| // fall through |
| } |
| return -1; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) { |
| final int type = attrs.type; |
| final boolean isRoundedCornerOverlay = |
| (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0; |
| |
| 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_DREAM: |
| case TYPE_INPUT_METHOD: |
| case TYPE_WALLPAPER: |
| case TYPE_PRESENTATION: |
| case TYPE_PRIVATE_PRESENTATION: |
| case TYPE_VOICE_INTERACTION: |
| case TYPE_ACCESSIBILITY_OVERLAY: |
| case TYPE_QS_DIALOG: |
| // 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 = mContext.getPackageManager().getApplicationInfoAsUser( |
| attrs.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; |
| } |
| |
| // check if user has enabled this operation. SecurityException will be thrown if this app |
| // has not been allowed by the user |
| final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName); |
| 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; |
| } |
| } |
| |
| @Override |
| public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) { |
| |
| // If this switch statement is modified, modify the comment in the declarations of |
| // the type in {@link WindowManager.LayoutParams} as well. |
| switch (attrs.type) { |
| default: |
| // These are the windows that by default are shown only to the user that created |
| // them. If this needs to be overridden, set |
| // {@link WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS} in |
| // {@link WindowManager.LayoutParams}. Note that permission |
| // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well. |
| if ((attrs.privateFlags & PRIVATE_FLAG_SHOW_FOR_ALL_USERS) == 0) { |
| return true; |
| } |
| break; |
| |
| // These are the windows that by default are shown to all users. However, to |
| // protect against spoofing, check permissions below. |
| case TYPE_APPLICATION_STARTING: |
| case TYPE_BOOT_PROGRESS: |
| case TYPE_DISPLAY_OVERLAY: |
| case TYPE_INPUT_CONSUMER: |
| case TYPE_KEYGUARD_DIALOG: |
| case TYPE_MAGNIFICATION_OVERLAY: |
| case TYPE_NAVIGATION_BAR: |
| case TYPE_NAVIGATION_BAR_PANEL: |
| case TYPE_PHONE: |
| case TYPE_POINTER: |
| case TYPE_PRIORITY_PHONE: |
| case TYPE_SEARCH_BAR: |
| case TYPE_STATUS_BAR: |
| case TYPE_STATUS_BAR_PANEL: |
| case TYPE_STATUS_BAR_SUB_PANEL: |
| case TYPE_SYSTEM_DIALOG: |
| case TYPE_VOLUME_OVERLAY: |
| case TYPE_PRESENTATION: |
| case TYPE_PRIVATE_PRESENTATION: |
| case TYPE_DOCK_DIVIDER: |
| break; |
| } |
| |
| // Check if third party app has set window to system window type. |
| return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED; |
| } |
| |
| @Override |
| public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, |
| boolean hasStatusBarServicePermission) { |
| |
| final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; |
| if (mScreenDecorWindows.contains(win)) { |
| if (!isScreenDecor) { |
| // No longer has the flag set, so remove from the set. |
| mScreenDecorWindows.remove(win); |
| } |
| } else if (isScreenDecor && hasStatusBarServicePermission) { |
| mScreenDecorWindows.add(win); |
| } |
| |
| switch (attrs.type) { |
| case TYPE_SYSTEM_OVERLAY: |
| case TYPE_SECURE_SYSTEM_OVERLAY: |
| // These types of windows can't receive input events. |
| attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; |
| attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; |
| break; |
| case TYPE_DREAM: |
| case TYPE_WALLPAPER: |
| // Dreams and wallpapers don't have an app window token and can thus not be |
| // letterboxed. Hence always let them extend under the cutout. |
| attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| break; |
| case TYPE_STATUS_BAR: |
| |
| // If the Keyguard is in a hidden state (occluded by another window), we force to |
| // remove the wallpaper and keyguard flag so that any change in-flight after setting |
| // the keyguard as occluded wouldn't set these flags again. |
| // See {@link #processKeyguardSetHiddenResultLw}. |
| if (mKeyguardOccluded) { |
| attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; |
| attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; |
| } |
| break; |
| |
| case TYPE_SCREENSHOT: |
| attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| break; |
| |
| case TYPE_TOAST: |
| // While apps should use the dedicated toast APIs to add such windows |
| // it possible legacy apps to add the window directly. Therefore, we |
| // make windows added directly by the app behave as a toast as much |
| // as possible in terms of timeout and animation. |
| if (attrs.hideTimeoutMilliseconds < 0 |
| || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) { |
| attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT; |
| } |
| attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; |
| break; |
| } |
| |
| if (attrs.type != TYPE_STATUS_BAR) { |
| // The status bar is the only window allowed to exhibit keyguard behavior. |
| attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; |
| } |
| } |
| |
| private int getImpliedSysUiFlagsForLayout(LayoutParams attrs) { |
| int impliedFlags = 0; |
| if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { |
| impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; |
| } |
| final boolean forceWindowDrawsStatusBarBackground = |
| (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; |
| if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 |
| || forceWindowDrawsStatusBarBackground |
| && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { |
| impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; |
| } |
| return impliedFlags; |
| } |
| |
| void readLidState() { |
| mLidState = mWindowManagerFuncs.getLidState(); |
| } |
| |
| private void readCameraLensCoverState() { |
| mCameraLensCoverState = mWindowManagerFuncs.getCameraLensCoverState(); |
| } |
| |
| private boolean isHidden(int accessibilityMode) { |
| switch (accessibilityMode) { |
| case 1: |
| return mLidState == LID_CLOSED; |
| case 2: |
| return mLidState == 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 void onOverlayChangedLw() { |
| onConfigurationChanged(); |
| } |
| |
| @Override |
| public void onConfigurationChanged() { |
| // TODO(multi-display): Define policy for secondary displays. |
| Context uiContext = getSystemUiContext(); |
| final Resources res = uiContext.getResources(); |
| |
| mStatusBarHeightForRotation[mPortraitRotation] = |
| mStatusBarHeightForRotation[mUpsideDownRotation] = res.getDimensionPixelSize( |
| com.android.internal.R.dimen.status_bar_height_portrait); |
| mStatusBarHeightForRotation[mLandscapeRotation] = |
| mStatusBarHeightForRotation[mSeascapeRotation] = res.getDimensionPixelSize( |
| com.android.internal.R.dimen.status_bar_height_landscape); |
| |
| // Height of the navigation bar when presented horizontally at bottom |
| mNavigationBarHeightForRotationDefault[mPortraitRotation] = |
| mNavigationBarHeightForRotationDefault[mUpsideDownRotation] = |
| res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height); |
| mNavigationBarHeightForRotationDefault[mLandscapeRotation] = |
| mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize( |
| com.android.internal.R.dimen.navigation_bar_height_landscape); |
| |
| // Width of the navigation bar when presented vertically along one side |
| mNavigationBarWidthForRotationDefault[mPortraitRotation] = |
| mNavigationBarWidthForRotationDefault[mUpsideDownRotation] = |
| mNavigationBarWidthForRotationDefault[mLandscapeRotation] = |
| mNavigationBarWidthForRotationDefault[mSeascapeRotation] = |
| res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width); |
| |
| if (ALTERNATE_CAR_MODE_NAV_SIZE) { |
| // Height of the navigation bar when presented horizontally at bottom |
| mNavigationBarHeightForRotationInCarMode[mPortraitRotation] = |
| mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] = |
| res.getDimensionPixelSize( |
| com.android.internal.R.dimen.navigation_bar_height_car_mode); |
| mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] = |
| mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize( |
| com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode); |
| |
| // Width of the navigation bar when presented vertically along one side |
| mNavigationBarWidthForRotationInCarMode[mPortraitRotation] = |
| mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] = |
| mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] = |
| mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] = |
| res.getDimensionPixelSize( |
| com.android.internal.R.dimen.navigation_bar_width_car_mode); |
| } |
| } |
| |
| @VisibleForTesting |
| Context getSystemUiContext() { |
| return ActivityThread.currentActivityThread().getSystemUiContext(); |
| } |
| |
| @Override |
| public int getMaxWallpaperLayer() { |
| return getWindowLayerFromTypeLw(TYPE_STATUS_BAR); |
| } |
| |
| private int getNavigationBarWidth(int rotation, int uiMode) { |
| if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { |
| return mNavigationBarWidthForRotationInCarMode[rotation]; |
| } else { |
| return mNavigationBarWidthForRotationDefault[rotation]; |
| } |
| } |
| |
| @Override |
| public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, |
| int displayId, DisplayCutout displayCutout) { |
| int width = fullWidth; |
| // TODO(multi-display): Support navigation bar on secondary displays. |
| if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) { |
| // For a basic navigation bar, when we are in landscape mode we place |
| // the navigation bar to the side. |
| if (mNavigationBarCanMove && fullWidth > fullHeight) { |
| width -= getNavigationBarWidth(rotation, uiMode); |
| } |
| } |
| if (displayCutout != null) { |
| width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight(); |
| } |
| return width; |
| } |
| |
| private int getNavigationBarHeight(int rotation, int uiMode) { |
| if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { |
| return mNavigationBarHeightForRotationInCarMode[rotation]; |
| } else { |
| return mNavigationBarHeightForRotationDefault[rotation]; |
| } |
| } |
| |
| @Override |
| public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, |
| int displayId, DisplayCutout displayCutout) { |
| int height = fullHeight; |
| // TODO(multi-display): Support navigation bar on secondary displays. |
| if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) { |
| // For a basic navigation bar, when we are in portrait mode we place |
| // the navigation bar to the bottom. |
| if (!mNavigationBarCanMove || fullWidth < fullHeight) { |
| height -= getNavigationBarHeight(rotation, uiMode); |
| } |
| } |
| if (displayCutout != null) { |
| height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom(); |
| } |
| return height; |
| } |
| |
| @Override |
| public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, |
| int displayId, DisplayCutout displayCutout) { |
| return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayId, |
| displayCutout); |
| } |
| |
| @Override |
| public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, |
| int displayId, DisplayCutout displayCutout) { |
| // There is a separate status bar at the top of the display. We don't count that as part |
| // of the fixed decor, since it can hide; however, for purposes of configurations, |
| // we do want to exclude it since applications can't generally use that part |
| // of the screen. |
| // TODO(multi-display): Support status bars on secondary displays. |
| if (displayId == DEFAULT_DISPLAY) { |
| int statusBarHeight = mStatusBarHeightForRotation[rotation]; |
| if (displayCutout != null) { |
| // If there is a cutout, it may already have accounted for some part of the status |
| // bar height. |
| statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop()); |
| } |
| return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayId, |
| displayCutout) - statusBarHeight; |
| } |
| return fullHeight; |
| } |
| |
| @Override |
| public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { |
| return attrs.type == TYPE_STATUS_BAR; |
| } |
| |
| @Override |
| public boolean canBeHiddenByKeyguardLw(WindowState win) { |
| switch (win.getAttrs().type) { |
| case TYPE_STATUS_BAR: |
| case TYPE_NAVIGATION_BAR: |
| case TYPE_WALLPAPER: |
| case TYPE_DREAM: |
| return false; |
| default: |
| // Hide only windows below the keyguard host window. |
| return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_STATUS_BAR); |
| } |
| } |
| |
| private boolean shouldBeHiddenByKeyguard(WindowState win, WindowState imeTarget) { |
| |
| // Keyguard visibility of window from activities are determined over activity visibility. |
| if (win.getAppToken() != null) { |
| return false; |
| } |
| |
| final LayoutParams attrs = win.getAttrs(); |
| final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw() && |
| ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0 |
| || !canBeHiddenByKeyguardLw(imeTarget)); |
| |
| // Show IME over the keyguard if the target allows it |
| boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this) |
| && showImeOverKeyguard; |
| |
| if (isKeyguardLocked() && isKeyguardOccluded()) { |
| // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded. |
| allowWhenLocked |= (attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 |
| // Show error dialogs over apps that are shown on lockscreen |
| || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0; |
| } |
| |
| boolean keyguardLocked = isKeyguardLocked(); |
| boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER |
| && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); |
| // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered |
| // hidden because it's in the process of hiding, but it's still being shown on screen. |
| // In that case, we want to continue hiding the IME until the windows have completed |
| // drawing. This way, we know that the IME can be safely shown since the other windows are |
| // now shown. |
| final boolean hideIme = |
| win.isInputMethodWindow() && (mAodShowing || !mWindowManagerDrawComplete); |
| return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY) |
| || hideDockDivider || hideIme; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, |
| CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, |
| int logo, int windowFlags, Configuration overrideConfig, int displayId) { |
| if (!SHOW_SPLASH_SCREENS) { |
| return null; |
| } |
| if (packageName == null) { |
| return null; |
| } |
| |
| WindowManager wm = null; |
| View view = null; |
| |
| try { |
| Context context = mContext; |
| if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName |
| + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme=" |
| + Integer.toHexString(theme)); |
| |
| // Obtain proper context to launch on the right display. |
| final Context displayContext = getDisplayContext(context, displayId); |
| if (displayContext == null) { |
| // Can't show splash screen on requested display, so skip showing at all. |
| return null; |
| } |
| context = displayContext; |
| |
| if (theme != context.getThemeResId() || labelRes != 0) { |
| try { |
| context = context.createPackageContext(packageName, CONTEXT_RESTRICTED); |
| context.setTheme(theme); |
| } catch (PackageManager.NameNotFoundException e) { |
| // Ignore |
| } |
| } |
| |
| if (overrideConfig != null && !overrideConfig.equals(EMPTY)) { |
| if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based" |
| + " on overrideConfig" + overrideConfig + " for splash screen"); |
| final Context overrideContext = context.createConfigurationContext(overrideConfig); |
| overrideContext.setTheme(theme); |
| final TypedArray typedArray = overrideContext.obtainStyledAttributes( |
| com.android.internal.R.styleable.Window); |
| final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0); |
| if (resId != 0 && overrideContext.getDrawable(resId) != null) { |
| // We want to use the windowBackground for the override context if it is |
| // available, otherwise we use the default one to make sure a themed starting |
| // window is displayed for the app. |
| if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig" |
| + overrideConfig + " to starting window resId=" + resId); |
| context = overrideContext; |
| } |
| typedArray.recycle(); |
| } |
| |
| final PhoneWindow win = new PhoneWindow(context); |
| win.setIsStartingWindow(true); |
| |
| CharSequence label = context.getResources().getText(labelRes, null); |
| // Only change the accessibility title if the label is localized |
| if (label != null) { |
| win.setTitle(label, true); |
| } else { |
| win.setTitle(nonLocalizedLabel, false); |
| } |
| |
| win.setType( |
| WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); |
| |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| // Assumes it's safe to show starting windows of launched apps while |
| // the keyguard is being hidden. This is okay because starting windows never show |
| // secret information. |
| if (mKeyguardOccluded) { |
| windowFlags |= FLAG_SHOW_WHEN_LOCKED; |
| } |
| } |
| |
| // Force the window flags: this is a fake window, so it is not really |
| // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM |
| // flag because we do know that the next window will take input |
| // focus, so we want to get the IME window up on top of us right away. |
| win.setFlags( |
| windowFlags| |
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| |
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| |
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, |
| windowFlags| |
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| |
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| |
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); |
| |
| win.setDefaultIcon(icon); |
| win.setDefaultLogo(logo); |
| |
| win.setLayout(WindowManager.LayoutParams.MATCH_PARENT, |
| WindowManager.LayoutParams.MATCH_PARENT); |
| |
| final WindowManager.LayoutParams params = win.getAttributes(); |
| params.token = appToken; |
| params.packageName = packageName; |
| params.windowAnimations = win.getWindowStyle().getResourceId( |
| com.android.internal.R.styleable.Window_windowAnimationStyle, 0); |
| params.privateFlags |= |
| WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED; |
| params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; |
| |
| if (!compatInfo.supportsScreen()) { |
| params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; |
| } |
| |
| params.setTitle("Splash Screen " + packageName); |
| addSplashscreenContent(win, context); |
| |
| wm = (WindowManager) context.getSystemService(WINDOW_SERVICE); |
| view = win.getDecorView(); |
| |
| if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for " |
| + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null)); |
| |
| wm.addView(view, params); |
| |
| // Only return the view if it was successfully added to the |
| // window manager... which we can tell by it having a parent. |
| return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null; |
| } catch (WindowManager.BadTokenException e) { |
| // ignore |
| Log.w(TAG, appToken + " already running, starting window not displayed. " + |
| e.getMessage()); |
| } catch (RuntimeException e) { |
| // don't crash if something else bad happens, for example a |
| // failure loading resources because we are loading from an app |
| // on external storage that has been unmounted. |
| Log.w(TAG, appToken + " failed creating starting window", e); |
| } finally { |
| if (view != null && view.getParent() == null) { |
| Log.w(TAG, "view not successfully added to wm, removing view"); |
| wm.removeViewImmediate(view); |
| } |
| } |
| |
| return null; |
| } |
| |
| private void addSplashscreenContent(PhoneWindow win, Context ctx) { |
| final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window); |
| final int resId = a.getResourceId(R.styleable.Window_windowSplashscreenContent, 0); |
| a.recycle(); |
| if (resId == 0) { |
| return; |
| } |
| final Drawable drawable = ctx.getDrawable(resId); |
| if (drawable == null) { |
| return; |
| } |
| |
| // We wrap this into a view so the system insets get applied to the drawable. |
| final View v = new View(ctx); |
| v.setBackground(drawable); |
| win.setContentView(v); |
| } |
| |
| /** Obtain proper context for showing splash screen on the provided display. */ |
| private Context getDisplayContext(Context context, int displayId) { |
| if (displayId == DEFAULT_DISPLAY) { |
| // The default context fits. |
| return context; |
| } |
| |
| final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE); |
| final Display targetDisplay = dm.getDisplay(displayId); |
| if (targetDisplay == null) { |
| // Failed to obtain the non-default display where splash screen should be shown, |
| // lets not show at all. |
| return null; |
| } |
| |
| return context.createDisplayContext(targetDisplay); |
| } |
| |
| /** |
| * Preflight adding a window to the system. |
| * |
| * Currently enforces that three window types are singletons: |
| * <ul> |
| * <li>STATUS_BAR_TYPE</li> |
| * <li>KEYGUARD_TYPE</li> |
| * </ul> |
| * |
| * @param win The window to be added |
| * @param attrs Information about the window to be added |
| * |
| * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, |
| * WindowManagerImpl.ADD_MULTIPLE_SINGLETON |
| */ |
| @Override |
| public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { |
| |
| if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { |
| mContext.enforceCallingOrSelfPermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, |
| "PhoneWindowManager"); |
| mScreenDecorWindows.add(win); |
| } |
| |
| switch (attrs.type) { |
| case TYPE_STATUS_BAR: |
| mContext.enforceCallingOrSelfPermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, |
| "PhoneWindowManager"); |
| if (mStatusBar != null) { |
| if (mStatusBar.isAlive()) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| } |
| mStatusBar = win; |
| mStatusBarController.setWindow(win); |
| setKeyguardOccludedLw(mKeyguardOccluded, true /* force */); |
| break; |
| case TYPE_NAVIGATION_BAR: |
| mContext.enforceCallingOrSelfPermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, |
| "PhoneWindowManager"); |
| if (mNavigationBar != null) { |
| if (mNavigationBar.isAlive()) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| } |
| mNavigationBar = win; |
| mNavigationBarController.setWindow(win); |
| mNavigationBarController.setOnBarVisibilityChangedListener( |
| mNavBarVisibilityListener, true); |
| if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); |
| break; |
| case TYPE_NAVIGATION_BAR_PANEL: |
| case TYPE_STATUS_BAR_PANEL: |
| case TYPE_STATUS_BAR_SUB_PANEL: |
| case TYPE_VOICE_INTERACTION_STARTING: |
| mContext.enforceCallingOrSelfPermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, |
| "PhoneWindowManager"); |
| break; |
| } |
| return ADD_OKAY; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void removeWindowLw(WindowState win) { |
| if (mStatusBar == win) { |
| mStatusBar = null; |
| mStatusBarController.setWindow(null); |
| } else if (mNavigationBar == win) { |
| mNavigationBar = null; |
| mNavigationBarController.setWindow(null); |
| } |
| mScreenDecorWindows.remove(win); |
| } |
| |
| static final boolean PRINT_ANIM = false; |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int selectAnimationLw(WindowState win, int transit) { |
| if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win |
| + ": transit=" + transit); |
| if (win == mStatusBar) { |
| final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; |
| final boolean expanded = win.getAttrs().height == MATCH_PARENT |
| && win.getAttrs().width == MATCH_PARENT; |
| if (isKeyguard || expanded) { |
| return -1; |
| } |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| return R.anim.dock_top_exit; |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_top_enter; |
| } |
| } else if (win == mNavigationBar) { |
| if (win.getAttrs().windowAnimations != 0) { |
| return 0; |
| } |
| // This can be on either the bottom or the right or the left. |
| if (mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| if (isKeyguardShowingAndNotOccluded()) { |
| return R.anim.dock_bottom_exit_keyguard; |
| } else { |
| return R.anim.dock_bottom_exit; |
| } |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_bottom_enter; |
| } |
| } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| return R.anim.dock_right_exit; |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_right_enter; |
| } |
| } else if (mNavigationBarPosition == NAV_BAR_LEFT) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| return R.anim.dock_left_exit; |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_left_enter; |
| } |
| } |
| } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) { |
| return selectDockedDividerAnimationLw(win, transit); |
| } |
| |
| if (transit == TRANSIT_PREVIEW_DONE) { |
| if (win.hasAppShownWindows()) { |
| if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT"); |
| return com.android.internal.R.anim.app_starting_exit; |
| } |
| } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen |
| && transit == TRANSIT_ENTER) { |
| // Special case: we are animating in a dream, while the keyguard |
| // is shown. We don't want an animation on the dream, because |
| // we need it shown immediately with the keyguard animating away |
| // to reveal it. |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| private int selectDockedDividerAnimationLw(WindowState win, int transit) { |
| int insets = mWindowManagerFuncs.getDockedDividerInsetsLw(); |
| |
| // If the divider is behind the navigation bar, don't animate. |
| final Rect frame = win.getFrameLw(); |
| final boolean behindNavBar = mNavigationBar != null |
| && ((mNavigationBarPosition == NAV_BAR_BOTTOM |
| && frame.top + insets >= mNavigationBar.getFrameLw().top) |
| || (mNavigationBarPosition == NAV_BAR_RIGHT |
| && frame.left + insets >= mNavigationBar.getFrameLw().left) |
| || (mNavigationBarPosition == NAV_BAR_LEFT |
| && frame.right - insets <= mNavigationBar.getFrameLw().right)); |
| final boolean landscape = frame.height() > frame.width(); |
| final boolean offscreenLandscape = landscape && (frame.right - insets <= 0 |
| || frame.left + insets >= win.getDisplayFrameLw().right); |
| final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0 |
| || frame.bottom + insets >= win.getDisplayFrameLw().bottom); |
| final boolean offscreen = offscreenLandscape || offscreenPortrait; |
| if (behindNavBar || offscreen) { |
| return 0; |
| } |
| if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { |
| return R.anim.fade_in; |
| } else if (transit == TRANSIT_EXIT) { |
| return R.anim.fade_out; |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public void selectRotationAnimationLw(int anim[]) { |
| // If the screen is off or non-interactive, force a jumpcut. |
| final boolean forceJumpcut = !mScreenOnFully || !okToAnimate(); |
| if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen=" |
| + mTopFullscreenOpaqueWindowState + " rotationAnimation=" |
| + (mTopFullscreenOpaqueWindowState == null ? |
| "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation) |
| + " forceJumpcut=" + forceJumpcut); |
| if (forceJumpcut) { |
| anim[0] = R.anim.rotation_animation_jump_exit; |
| anim[1] = R.anim.rotation_animation_enter; |
| return; |
| } |
| if (mTopFullscreenOpaqueWindowState != null) { |
| int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint(); |
| if (animationHint < 0 && mTopIsFullscreen) { |
| animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation; |
| } |
| switch (animationHint) { |
| case ROTATION_ANIMATION_CROSSFADE: |
| case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless. |
| anim[0] = R.anim.rotation_animation_xfade_exit; |
| anim[1] = R.anim.rotation_animation_enter; |
| break; |
| case ROTATION_ANIMATION_JUMPCUT: |
| anim[0] = R.anim.rotation_animation_jump_exit; |
| anim[1] = R.anim.rotation_animation_enter; |
| break; |
| case ROTATION_ANIMATION_ROTATE: |
| default: |
| anim[0] = anim[1] = 0; |
| break; |
| } |
| } else { |
| anim[0] = anim[1] = 0; |
| } |
| } |
| |
| @Override |
| public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId, |
| boolean forceDefault) { |
| switch (exitAnimId) { |
| case R.anim.rotation_animation_xfade_exit: |
| case R.anim.rotation_animation_jump_exit: |
| // These are the only cases that matter. |
| if (forceDefault) { |
| return false; |
| } |
| int anim[] = new int[2]; |
| selectRotationAnimationLw(anim); |
| return (exitAnimId == anim[0] && enterAnimId == anim[1]); |
| default: |
| return true; |
| } |
| } |
| |
| @Override |
| public Animation createHiddenByKeyguardExit(boolean onWallpaper, |
| boolean goingToNotificationShade) { |
| if (goingToNotificationShade) { |
| return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_behind_enter_fade_in); |
| } |
| |
| AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(mContext, onWallpaper ? |
| R.anim.lock_screen_behind_enter_wallpaper : |
| R.anim.lock_screen_behind_enter); |
| |
| // TODO: Use XML interpolators when we have log interpolators available in XML. |
| final List<Animation> animations = set.getAnimations(); |
| for (int i = animations.size() - 1; i >= 0; --i) { |
| animations.get(i).setInterpolator(mLogDecelerateInterpolator); |
| } |
| |
| return set; |
| } |
| |
| |
| @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); |
| } |
| |
| 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, |
| }; |
| |
| /** {@inheritDoc} */ |
| @Override |
| public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { |
| final boolean keyguardOn = keyguardOn(); |
| final int keyCode = event.getKeyCode(); |
| final int repeatCount = event.getRepeatCount(); |
| final int metaState = event.getMetaState(); |
| final int flags = event.getFlags(); |
| final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; |
| final boolean canceled = event.isCanceled(); |
| |
| if (DEBUG_INPUT) { |
| Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" |
| + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed |
| + " canceled=" + canceled); |
| } |
| |
| // If we think we might have a volume down & power key chord on the way |
| // but we're not sure, then tell the dispatcher to wait a little while and |
| // try again later before dispatching. |
| if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) { |
| if (mScreenshotChordVolumeDownKeyTriggered && !mScreenshotChordPowerKeyTriggered) { |
| final long now = SystemClock.uptimeMillis(); |
| final long timeoutTime = mScreenshotChordVolumeDownKeyTime |
| + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; |
| if (now < timeoutTime) { |
| return timeoutTime - now; |
| } |
| } |
| if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN |
| && mScreenshotChordVolumeDownKeyConsumed) { |
| if (!down) { |
| mScreenshotChordVolumeDownKeyConsumed = false; |
| } |
| return -1; |
| } |
| } |
| |
| // If an accessibility shortcut might be partially complete, hold off dispatching until we |
| // know if it is complete or not |
| if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false) |
| && (flags & KeyEvent.FLAG_FALLBACK) == 0) { |
| if (mScreenshotChordVolumeDownKeyTriggered ^ mA11yShortcutChordVolumeUpKeyTriggered) { |
| final long now = SystemClock.uptimeMillis(); |
| final long timeoutTime = (mScreenshotChordVolumeDownKeyTriggered |
| ? mScreenshotChordVolumeDownKeyTime : mA11yShortcutChordVolumeUpKeyTime) |
| + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; |
| if (now < timeoutTime) { |
| return timeoutTime - now; |
| } |
| } |
| if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mScreenshotChordVolumeDownKeyConsumed) { |
| if (!down) { |
| mScreenshotChordVolumeDownKeyConsumed = false; |
| } |
| return -1; |
| } |
| if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) { |
| if (!down) { |
| mA11yShortcutChordVolumeUpKeyConsumed = false; |
| } |
| return -1; |
| } |
| } |
| |
| // If a ringer toggle chord could be on the way but we're not sure, then tell the dispatcher |
| // to wait a little while and try again later before dispatching. |
| if (mRingerToggleChord != VOLUME_HUSH_OFF && (flags & KeyEvent.FLAG_FALLBACK) == 0) { |
| if (mA11yShortcutChordVolumeUpKeyTriggered && !mScreenshotChordPowerKeyTriggered) { |
| final long now = SystemClock.uptimeMillis(); |
| final long timeoutTime = mA11yShortcutChordVolumeUpKeyTime |
| + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; |
| if (now < timeoutTime) { |
| return timeoutTime - now; |
| } |
| } |
| if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) { |
| if (!down) { |
| mA11yShortcutChordVolumeUpKeyConsumed = false; |
| } |
| return -1; |
| } |
| } |
| |
| // 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; |
| } |
| |
| // 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. |
| if (keyCode == KeyEvent.KEYCODE_HOME) { |
| |
| // 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) { |
| cancelPreloadRecentApps(); |
| |
| mHomePressed = false; |
| if (mHomeConsumed) { |
| mHomeConsumed = false; |
| return -1; |
| } |
| |
| if (canceled) { |
| Log.i(TAG, "Ignoring HOME; event canceled."); |
| return -1; |
| } |
| |
| // Delay handling home if a double-tap is possible. |
| if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) { |
| mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case |
| mHomeDoubleTapPending = true; |
| mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable, |
| ViewConfiguration.getDoubleTapTimeout()); |
| return -1; |
| } |
| |
| handleShortPressOnHome(); |
| return -1; |
| } |
| |
| // If a system window has focus, then it doesn't make sense |
| // right now to interact with applications. |
| WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null; |
| if (attrs != null) { |
| final int type = attrs.type; |
| if (type == TYPE_KEYGUARD_DIALOG |
| || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { |
| // the "app" is keyguard, so give it the key |
| return 0; |
| } |
| final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length; |
| for (int i=0; i<typeCount; i++) { |
| if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) { |
| // don't do anything, but also don't pass it to the app |
| return -1; |
| } |
| } |
| } |
| |
| // Remember that home is pressed and handle special actions. |
| if (repeatCount == 0) { |
| mHomePressed = true; |
| if (mHomeDoubleTapPending) { |
| mHomeDoubleTapPending = false; |
| mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); |
| handleDoubleTapOnHome(); |
| } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) { |
| preloadRecentApps(); |
| } |
| } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { |
| if (!keyguardOn) { |
| handleLongPressOnHome(event.getDeviceId()); |
| } |
| } |
| return -1; |
| } else if (keyCode == KeyEvent.KEYCODE_MENU) { |
| // Hijack modified menu keys for debugging features |
| final int chordBug = KeyEvent.META_SHIFT_ON; |
| |
| if (down && repeatCount == 0) { |
| if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { |
| Intent intent = new Intent(Intent.ACTION_BUG_REPORT); |
| mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, |
| null, null, null, 0, null, null); |
| return -1; |
| } |
| } |
| } else if (keyCode == KeyEvent.KEYCODE_SEARCH) { |
| if (down) { |
| if (repeatCount == 0) { |
| mSearchKeyShortcutPending = true; |
| mConsumeSearchKeyUp = false; |
| } |
| } else { |
| mSearchKeyShortcutPending = false; |
| if (mConsumeSearchKeyUp) { |
| mConsumeSearchKeyUp = false; |
| return -1; |
| } |
| } |
| return 0; |
| } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { |
| if (!keyguardOn) { |
| if (down && repeatCount == 0) { |
| preloadRecentApps(); |
| } else if (!down) { |
| toggleRecentApps(); |
| } |
| } |
| return -1; |
| } else if (keyCode == KeyEvent.KEYCODE_N && event.isMetaPressed()) { |
| if (down) { |
| IStatusBarService service = getStatusBarService(); |
| if (service != null) { |
| try { |
| service.expandNotificationsPanel(); |
| } catch (RemoteException e) { |
| // do nothing. |
| } |
| } |
| } |
| } else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed() |
| && event.isCtrlPressed()) { |
| if (down && repeatCount == 0) { |
| int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION |
| : TAKE_SCREENSHOT_FULLSCREEN; |
| mScreenshotRunnable.setScreenshotType(type); |
| mHandler.post(mScreenshotRunnable); |
| return -1; |
| } |
| } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) { |
| if (down && repeatCount == 0 && !isKeyguardLocked()) { |
| toggleKeyboardShortcutsMenu(event.getDeviceId()); |
| } |
| } else if (keyCode == KeyEvent.KEYCODE_ASSIST) { |
| Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing"); |
| return -1; |
| } else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) { |
| Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in interceptKeyBeforeQueueing"); |
| return -1; |
| } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) { |
| if (down && repeatCount == 0) { |
| mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); |
| mHandler.post(mScreenshotRunnable); |
| } |
| return -1; |
| } else if (keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP |
| || keyCode == 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 min = mPowerManager.getMinimumScreenBrightnessSetting(); |
| int max = mPowerManager.getMaximumScreenBrightnessSetting(); |
| int step = (max - min + BRIGHTNESS_STEPS - 1) / BRIGHTNESS_STEPS * direction; |
| int brightness = Settings.System.getIntForUser(mContext.getContentResolver(), |
| Settings.System.SCREEN_BRIGHTNESS, |
| mPowerManager.getDefaultScreenBrightnessSetting(), |
| UserHandle.USER_CURRENT_OR_SELF); |
| brightness += step; |
| // Make sure we don't go beyond the limits. |
| brightness = Math.min(max, brightness); |
| brightness = Math.max(min, brightness); |
| |
| Settings.System.putIntForUser(mContext.getContentResolver(), |
| Settings.System.SCREEN_BRIGHTNESS, brightness, |
| UserHandle.USER_CURRENT_OR_SELF); |
| startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), |
| UserHandle.CURRENT_OR_SELF); |
| } |
| return -1; |
| } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP |
| || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN |
| || keyCode == 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 -1; |
| } |
| |
| // 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 (mPersistentVrModeEnabled) { |
| final InputDevice d = event.getDevice(); |
| if (d != null && !d.isExternal()) { |
| return -1; |
| } |
| } |
| } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { |
| // Pass through keyboard navigation keys. |
| return 0; |
| } else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) { |
| return -1; |
| } else if (mHasFeatureLeanback && interceptAccessibilityGestureTv(keyCode, down)) { |
| return -1; |
| } else if (keyCode == 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(); |
| } |
| return -1; |
| } |
| |
| // Toggle Caps Lock on META-ALT. |
| boolean actionTriggered = false; |
| if (KeyEvent.isModifierKey(keyCode)) { |
| if (!mPendingCapsLockToggle) { |
| // Start tracking meta state for combo. |
| mInitialMetaState = mMetaState; |
| mPendingCapsLockToggle = true; |
| } else if (event.getAction() == KeyEvent.ACTION_UP) { |
| int altOnMask = mMetaState & KeyEvent.META_ALT_MASK; |
| int metaOnMask = mMetaState & KeyEvent.META_META_MASK; |
| |
| // Check for Caps Lock toggle |
| if ((metaOnMask != 0) && (altOnMask != 0)) { |
| // Check if nothing else is pressed |
| if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) { |
| // Handle Caps Lock Toggle |
| mInputManagerInternal.toggleCapsLock(event.getDeviceId()); |
| actionTriggered = true; |
| } |
| } |
| |
| // Always stop tracking when key goes up. |
| mPendingCapsLockToggle = false; |
| } |
| } |
| // Store current meta state to be able to evaluate it later. |
| mMetaState = metaState; |
| |
| if (actionTriggered) { |
| return -1; |
| } |
| |
| if (KeyEvent.isMetaKey(keyCode)) { |
| if (down) { |
| mPendingMetaAction = true; |
| } else if (mPendingMetaAction) { |
| launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId()); |
| } |
| return -1; |
| } |
| |
| // Shortcuts are invoked through Search+key, so intercept those here |
| // Any printing key that is chorded with Search should be consumed |
| // even if no shortcut was invoked. This prevents text from being |
| // inadvertently inserted when using a keyboard that has built-in macro |
| // shortcut keys (that emit Search+x) and some of them are not registered. |
| if (mSearchKeyShortcutPending) { |
| final KeyCharacterMap kcm = event.getKeyCharacterMap(); |
| if (kcm.isPrintingKey(keyCode)) { |
| mConsumeSearchKeyUp = true; |
| mSearchKeyShortcutPending = false; |
| if (down && repeatCount == 0 && !keyguardOn) { |
| Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, metaState); |
| if (shortcutIntent != null) { |
| shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| startActivityAsUser(shortcutIntent, UserHandle.CURRENT); |
| dismissKeyboardShortcutsMenu(); |
| } catch (ActivityNotFoundException ex) { |
| Slog.w(TAG, "Dropping shortcut key combination because " |
| + "the activity to which it is registered was not found: " |
| + "SEARCH+" + KeyEvent.keyCodeToString(keyCode), ex); |
| } |
| } else { |
| Slog.i(TAG, "Dropping unregistered shortcut key combination: " |
| + "SEARCH+" + KeyEvent.keyCodeToString(keyCode)); |
| } |
| } |
| return -1; |
| } |
| } |
| |
| // Invoke shortcuts using Meta. |
| if (down && repeatCount == 0 && !keyguardOn |
| && (metaState & KeyEvent.META_META_ON) != 0) { |
| final KeyCharacterMap kcm = event.getKeyCharacterMap(); |
| if (kcm.isPrintingKey(keyCode)) { |
| Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, |
| metaState & ~(KeyEvent.META_META_ON |
| | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON)); |
| if (shortcutIntent != null) { |
| shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| startActivityAsUser(shortcutIntent, UserHandle.CURRENT); |
| dismissKeyboardShortcutsMenu(); |
| } catch (ActivityNotFoundException ex) { |
| Slog.w(TAG, "Dropping shortcut key combination because " |
| + "the activity to which it is registered was not found: " |
| + "META+" + KeyEvent.keyCodeToString(keyCode), ex); |
| } |
| return -1; |
| } |
| } |
| } |
| |
| // Handle application launch keys. |
| if (down && repeatCount == 0 && !keyguardOn) { |
| String category = sApplicationLaunchKeyCategories.get(keyCode); |
| if (category != null) { |
| Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| startActivityAsUser(intent, UserHandle.CURRENT); |
| dismissKeyboardShortcutsMenu(); |
| } catch (ActivityNotFoundException ex) { |
| Slog.w(TAG, "Dropping application launch key because " |
| + "the activity to which it is registered was not found: " |
| + "keyCode=" + keyCode + ", category=" + category, ex); |
| } |
| return -1; |
| } |
| } |
| |
| // Display task switcher for ALT-TAB. |
| if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { |
| if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { |
| final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; |
| if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { |
| mRecentAppsHeldModifiers = shiftlessModifiers; |
| showRecentApps(true); |
| return -1; |
| } |
| } |
| } else if (!down && mRecentAppsHeldModifiers != 0 |
| && (metaState & mRecentAppsHeldModifiers) == 0) { |
| mRecentAppsHeldModifiers = 0; |
| hideRecentApps(true, false); |
| } |
| |
| // Handle keyboard layout switching. |
| // TODO: Deprecate this behavior when we fully migrate to IME subtype-based layout rotation. |
| if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_SPACE |
| && ((metaState & KeyEvent.META_CTRL_MASK) != 0)) { |
| int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; |
| mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); |
| return -1; |
| } |
| |
| // Handle input method switching. |
| if (down && repeatCount == 0 |
| && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH |
| || (keyCode == KeyEvent.KEYCODE_SPACE |
| && (metaState & KeyEvent.META_META_MASK) != 0))) { |
| final boolean forwardDirection = (metaState & KeyEvent.META_SHIFT_MASK) == 0; |
| mWindowManagerFuncs.switchInputMethod(forwardDirection); |
| return -1; |
| } |
| if (mLanguageSwitchKeyPressed && !down |
| && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH |
| || keyCode == KeyEvent.KEYCODE_SPACE)) { |
| mLanguageSwitchKeyPressed = false; |
| return -1; |
| } |
| |
| if (isValidGlobalKey(keyCode) |
| && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { |
| return -1; |
| } |
| |
| if (down) { |
| long shortcutCode = keyCode; |
| if (event.isCtrlPressed()) { |
| shortcutCode |= ((long) KeyEvent.META_CTRL_ON) << Integer.SIZE; |
| } |
| |
| if (event.isAltPressed()) { |
| shortcutCode |= ((long) KeyEvent.META_ALT_ON) << Integer.SIZE; |
| } |
| |
| if (event.isShiftPressed()) { |
| shortcutCode |= ((long) KeyEvent.META_SHIFT_ON) << Integer.SIZE; |
| } |
| |
| if (event.isMetaPressed()) { |
| shortcutCode |= ((long) KeyEvent.META_META_ON) << Integer.SIZE; |
| } |
| |
| IShortcutService shortcutService = mShortcutKeyServices.get(shortcutCode); |
| if (shortcutService != null) { |
| try { |
| if (isUserSetupComplete()) { |
| shortcutService.notifyShortcutKeyPressed(shortcutCode); |
| } |
| } catch (RemoteException e) { |
| mShortcutKeyServices.delete(shortcutCode); |
| } |
| return -1; |
| } |
| } |
| |
| // Reserve all the META modifier combos for system behavior |
| if ((metaState & KeyEvent.META_META_ON) != 0) { |
| return -1; |
| } |
| |
| // Let the application handle the key. |
| return 0; |
| } |
| |
| /** |
| * TV only: recognizes a remote control gesture for capturing a bug report. |
| */ |
| private boolean interceptBugreportGestureTv(int keyCode, boolean down) { |
| // The bugreport capture chord is a long press on DPAD CENTER and BACK simultaneously. |
| if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { |
| mBugreportTvKey1Pressed = down; |
| } else if (keyCode == KeyEvent.KEYCODE_BACK) { |
| mBugreportTvKey2Pressed = down; |
| } |
| |
| if (mBugreportTvKey1Pressed && mBugreportTvKey2Pressed) { |
| if (!mBugreportTvScheduled) { |
| mBugreportTvScheduled = true; |
| Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV); |
| msg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS); |
| } |
| } else if (mBugreportTvScheduled) { |
| mHandler.removeMessages(MSG_BUGREPORT_TV); |
| mBugreportTvScheduled = false; |
| } |
| |
| return mBugreportTvScheduled; |
| } |
| |
| /** |
| * TV only: recognizes a remote control gesture as Accessibility shortcut. |
| * Shortcut: Long press (BACK + DPAD_DOWN) |
| */ |
| private boolean interceptAccessibilityGestureTv(int keyCode, boolean down) { |
| if (keyCode == KeyEvent.KEYCODE_BACK) { |
| mAccessibilityTvKey1Pressed = down; |
| } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { |
| mAccessibilityTvKey2Pressed = down; |
| } |
| |
| if (mAccessibilityTvKey1Pressed && mAccessibilityTvKey2Pressed) { |
| if (!mAccessibilityTvScheduled) { |
| mAccessibilityTvScheduled = true; |
| Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV); |
| msg.setAsynchronous(true); |
| mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout()); |
| } |
| } else if (mAccessibilityTvScheduled) { |
| mHandler.removeMessages(MSG_ACCESSIBILITY_TV); |
| mAccessibilityTvScheduled = false; |
| } |
| |
| return mAccessibilityTvScheduled; |
| } |
| |
| private void requestFullBugreport() { |
| if ("1".equals(SystemProperties.get("ro.debuggable")) |
| || Settings.Global.getInt(mContext.getContentResolver(), |
| Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) { |
| try { |
| ActivityManager.getService() |
| .requestBugReport(ActivityManager.BUGREPORT_OPTION_FULL); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Error taking bugreport", e); |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) { |
| // Note: This method is only called if the initial down was unhandled. |
| if (DEBUG_INPUT) { |
| Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction() |
| + ", flags=" + event.getFlags() |
| + ", keyCode=" + event.getKeyCode() |
| + ", scanCode=" + event.getScanCode() |
| + ", metaState=" + event.getMetaState() |
| + ", repeatCount=" + event.getRepeatCount() |
| + ", policyFlags=" + policyFlags); |
| } |
| |
| 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(), null); |
| |
| if (!interceptFallback(win, 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 interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) { |
| int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); |
| if ((actions & ACTION_PASS_TO_USER) != 0) { |
| long delayMillis = interceptKeyBeforeDispatching( |
| win, fallbackEvent, policyFlags); |
| if (delayMillis == 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService) |
| throws RemoteException { |
| synchronized (mLock) { |
| IShortcutService service = mShortcutKeyServices.get(shortcutCode); |
| if (service != null && service.asBinder().pingBinder()) { |
| throw new RemoteException("Key already exists."); |
| } |
| |
| mShortcutKeyServices.put(shortcutCode, shortcutService); |
| } |
| } |
| |
| @Override |
| public void onKeyguardOccludedChangedLw(boolean occluded) { |
| if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) { |
| mPendingKeyguardOccluded = occluded; |
| mKeyguardOccludedChanged = true; |
| } else { |
| setKeyguardOccludedLw(occluded, false /* force */); |
| } |
| } |
| |
| private int handleStartTransitionForKeyguardLw(int transit, long duration) { |
| if (mKeyguardOccludedChanged) { |
| if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded=" |
| + mPendingKeyguardOccluded); |
| mKeyguardOccludedChanged = false; |
| if (setKeyguardOccludedLw(mPendingKeyguardOccluded, false /* force */)) { |
| return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| } |
| if (AppTransition.isKeyguardGoingAwayTransit(transit)) { |
| if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation"); |
| startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration); |
| } |
| return 0; |
| } |
| |
| private void launchAssistLongPressAction() { |
| performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); |
| sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); |
| |
| // launch the search activity |
| Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| // TODO: This only stops the factory-installed search manager. |
| // Need to formalize an API to handle others |
| SearchManager searchManager = getSearchManager(); |
| if (searchManager != null) { |
| searchManager.stopSearch(); |
| } |
| startActivityAsUser(intent, UserHandle.CURRENT); |
| } catch (ActivityNotFoundException e) { |
| Slog.w(TAG, "No activity to handle assist long press action.", e); |
| } |
| } |
| |
| private void launchAssistAction(String hint, int deviceId) { |
| sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); |
| if (!isUserSetupComplete()) { |
| // Disable opening assist window during setup |
| return; |
| } |
| Bundle args = null; |
| if (deviceId > Integer.MIN_VALUE) { |
| args = new Bundle(); |
| args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId); |
| } |
| if ((mContext.getResources().getConfiguration().uiMode |
| & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { |
| // On TV, use legacy handling until assistants are implemented in the proper way. |
| ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) |
| .launchLegacyAssist(hint, UserHandle.myUserId(), args); |
| } else { |
| if (hint != null) { |
| if (args == null) { |
| args = new Bundle(); |
| } |
| args.putBoolean(hint, true); |
| } |
| StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); |
| if (statusbar != null) { |
| statusbar.startAssist(args); |
| } |
| } |
| } |
| |
| private void startActivityAsUser(Intent intent, UserHandle handle) { |
| if (isUserSetupComplete()) { |
| mContext.startActivityAsUser(intent, handle); |
| } else { |
| Slog.i(TAG, "Not starting activity because user setup is in progress: " + intent); |
| } |
| } |
| |
| private SearchManager getSearchManager() { |
| if (mSearchManager == null) { |
| mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); |
| } |
| return mSearchManager; |
| } |
| |
| 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 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); |
| } |
| } |
| |
| void launchHomeFromHotKey() { |
| launchHomeFromHotKey(true /* awakenFromDreams */, true /*respectKeyguard*/); |
| } |
| |
| /** |
| * A home key -> launch home action was detected. Take the appropriate action |
| * given the situation with the keyguard. |
| */ |
| void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) { |
| // Abort possibly stuck animations. |
| mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); |
| |
| if (respectKeyguard) { |
| if (isKeyguardShowingAndNotOccluded()) { |
| // don't launch home if keyguard showing |
| return; |
| } |
| |
| if (!mKeyguardOccluded && 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) { |
| startDockOrHome(true /*fromHomeKey*/, awakenFromDreams); |
| } |
| } |
| }); |
| 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(true /*fromHomeKey*/, awakenFromDreams); |
| } |
| } |
| |
| private final Runnable mClearHideNavigationFlag = new Runnable() { |
| @Override |
| public void run() { |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| // Clear flags. |
| mForceClearedSystemUiFlags &= |
| ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; |
| } |
| mWindowManagerFuncs.reevaluateStatusBarVisibility(); |
| } |
| }; |
| |
| /** |
| * Input handler used while nav bar is hidden. Captures any touch on the screen, |
| * to determine when the nav bar should be shown and prevent applications from |
| * receiving those touches. |
| */ |
| final class HideNavInputEventReceiver extends InputEventReceiver { |
| public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { |
| super(inputChannel, looper); |
| } |
| |
| @Override |
| public void onInputEvent(InputEvent event, int displayId) { |
| boolean handled = false; |
| try { |
| if (event instanceof MotionEvent |
| && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { |
| final MotionEvent motionEvent = (MotionEvent)event; |
| if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { |
| // When the user taps down, we re-show the nav bar. |
| boolean changed = false; |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| if (mInputConsumer == null) { |
| return; |
| } |
| // Any user activity always causes us to show the |
| // navigation controls, if they had been hidden. |
| // We also clear the low profile and only content |
| // flags so that tapping on the screen will atomically |
| // restore all currently hidden screen decorations. |
| int newVal = mResettingSystemUiFlags | |
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | |
| View.SYSTEM_UI_FLAG_LOW_PROFILE | |
| View.SYSTEM_UI_FLAG_FULLSCREEN; |
| if (mResettingSystemUiFlags != newVal) { |
| mResettingSystemUiFlags = newVal; |
| changed = true; |
| } |
| // We don't allow the system's nav bar to be hidden |
| // again for 1 second, to prevent applications from |
| // spamming us and keeping it from being shown. |
| newVal = mForceClearedSystemUiFlags | |
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; |
| if (mForceClearedSystemUiFlags != newVal) { |
| mForceClearedSystemUiFlags = newVal; |
| changed = true; |
| mHandler.postDelayed(mClearHideNavigationFlag, 1000); |
| } |
| } |
| if (changed) { |
| mWindowManagerFuncs.reevaluateStatusBarVisibility(); |
| } |
| } |
| } |
| } finally { |
| finishInputEvent(event, handled); |
| } |
| } |
| } |
| |
| @Override |
| public void setRecentsVisibilityLw(boolean visible) { |
| mRecentsVisible = visible; |
| } |
| |
| @Override |
| public void setPipVisibilityLw(boolean visible) { |
| mPictureInPictureVisible = visible; |
| } |
| |
| @Override |
| public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) { |
| mNavBarVirtualKeyHapticFeedbackEnabled = enabled; |
| } |
| |
| @Override |
| public int adjustSystemUiVisibilityLw(int visibility) { |
| mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); |
| mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); |
| |
| // Reset any bits in mForceClearingStatusBarVisibility that |
| // are now clear. |
| mResettingSystemUiFlags &= visibility; |
| // Clear any bits in the new visibility that are currently being |
| // force cleared, before reporting it. |
| return visibility & ~mResettingSystemUiFlags |
| & ~mForceClearedSystemUiFlags; |
| } |
| |
| @Override |
| // TODO: Should probably be moved into DisplayFrames. |
| public boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds, |
| DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets, Rect outStableInsets, |
| Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) { |
| final int fl = PolicyControl.getWindowFlags(null, attrs); |
| final int pfl = attrs.privateFlags; |
| final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); |
| final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); |
| final int displayRotation = displayFrames.mRotation; |
| final int displayWidth = displayFrames.mDisplayWidth; |
| final int displayHeight = displayFrames.mDisplayHeight; |
| |
| final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl); |
| if (useOutsets) { |
| int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); |
| if (outset > 0) { |
| if (displayRotation == Surface.ROTATION_0) { |
| outOutsets.bottom += outset; |
| } else if (displayRotation == Surface.ROTATION_90) { |
| outOutsets.right += outset; |
| } else if (displayRotation == Surface.ROTATION_180) { |
| outOutsets.top += outset; |
| } else if (displayRotation == Surface.ROTATION_270) { |
| outOutsets.left += outset; |
| } |
| } |
| } |
| |
| final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; |
| final boolean layoutInScreenAndInsetDecor = layoutInScreen && |
| (fl & FLAG_LAYOUT_INSET_DECOR) != 0; |
| final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; |
| |
| if (layoutInScreenAndInsetDecor && !screenDecor) { |
| int availRight, availBottom; |
| if (canHideNavigationBar() && |
| (sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { |
| outFrame.set(displayFrames.mUnrestricted); |
| availRight = displayFrames.mUnrestricted.right; |
| availBottom = displayFrames.mUnrestricted.bottom; |
| } else { |
| outFrame.set(displayFrames.mRestricted); |
| availRight = displayFrames.mRestricted.right; |
| availBottom = displayFrames.mRestricted.bottom; |
| } |
| outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top, |
| availRight - displayFrames.mStable.right, |
| availBottom - displayFrames.mStable.bottom); |
| |
| if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { |
| if ((fl & FLAG_FULLSCREEN) != 0) { |
| outContentInsets.set(displayFrames.mStableFullscreen.left, |
| displayFrames.mStableFullscreen.top, |
| availRight - displayFrames.mStableFullscreen.right, |
| availBottom - displayFrames.mStableFullscreen.bottom); |
| } else { |
| outContentInsets.set(outStableInsets); |
| } |
| } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) { |
| outContentInsets.setEmpty(); |
| } else { |
| outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top, |
| availRight - displayFrames.mCurrent.right, |
| availBottom - displayFrames.mCurrent.bottom); |
| } |
| |
| if (taskBounds != null) { |
| calculateRelevantTaskInsets(taskBounds, outContentInsets, |
| displayWidth, displayHeight); |
| calculateRelevantTaskInsets(taskBounds, outStableInsets, |
| displayWidth, displayHeight); |
| outFrame.intersect(taskBounds); |
| } |
| outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) |
| .getDisplayCutout()); |
| return mForceShowSystemBars; |
| } else { |
| if (layoutInScreen) { |
| outFrame.set(displayFrames.mUnrestricted); |
| } else { |
| outFrame.set(displayFrames.mStable); |
| } |
| if (taskBounds != null) { |
| outFrame.intersect(taskBounds); |
| } |
| |
| outContentInsets.setEmpty(); |
| outStableInsets.setEmpty(); |
| outDisplayCutout.set(DisplayCutout.NO_CUTOUT); |
| return mForceShowSystemBars; |
| } |
| } |
| |
| /** |
| * For any given task bounds, the insets relevant for these bounds given the insets relevant |
| * for the entire display. |
| */ |
| private void calculateRelevantTaskInsets(Rect taskBounds, Rect inOutInsets, int displayWidth, |
| int displayHeight) { |
| mTmpRect.set(0, 0, displayWidth, displayHeight); |
| mTmpRect.inset(inOutInsets); |
| mTmpRect.intersect(taskBounds); |
| int leftInset = mTmpRect.left - taskBounds.left; |
| int topInset = mTmpRect.top - taskBounds.top; |
| int rightInset = taskBounds.right - mTmpRect.right; |
| int bottomInset = taskBounds.bottom - mTmpRect.bottom; |
| inOutInsets.set(leftInset, topInset, rightInset, bottomInset); |
| } |
| |
| private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) { |
| return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN |
| | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { |
| displayFrames.onBeginLayout(); |
| // TODO(multi-display): This doesn't seem right...Maybe only apply to default display? |
| mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); |
| mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); |
| mDockLayer = 0x10000000; |
| mStatusBarLayer = -1; |
| |
| // start with the current dock rect, which will be (0,0,displayWidth,displayHeight) |
| final Rect pf = mTmpParentFrame; |
| final Rect df = mTmpDisplayFrame; |
| final Rect of = mTmpOverscanFrame; |
| final Rect vf = mTmpVisibleFrame; |
| final Rect dcf = mTmpDecorFrame; |
| vf.set(displayFrames.mDock); |
| of.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| pf.set(displayFrames.mDock); |
| dcf.setEmpty(); // Decor frame N/A for system bars. |
| |
| if (displayFrames.mDisplayId == DEFAULT_DISPLAY) { |
| // For purposes of putting out fake window up to steal focus, we will |
| // drive nav being hidden only by whether it is requested. |
| final int sysui = mLastSystemUiFlags; |
| boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; |
| boolean navTranslucent = (sysui |
| & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; |
| boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; |
| boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; |
| boolean navAllowedHidden = immersive || immersiveSticky; |
| navTranslucent &= !immersiveSticky; // transient trumps translucent |
| boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded; |
| if (!isKeyguardShowing) { |
| navTranslucent &= areTranslucentBarsAllowed(); |
| } |
| boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null |
| && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0; |
| |
| // When the navigation bar isn't visible, we put up a fake input window to catch all |
| // touch events. This way we can detect when the user presses anywhere to bring back the |
| // nav bar and ensure the application doesn't see the event. |
| if (navVisible || navAllowedHidden) { |
| if (mInputConsumer != null) { |
| mHandler.sendMessage( |
| mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); |
| mInputConsumer = null; |
| } |
| } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { |
| mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(), |
| INPUT_CONSUMER_NAVIGATION, |
| (channel, looper) -> new HideNavInputEventReceiver(channel, looper)); |
| // As long as mInputConsumer is active, hover events are not dispatched to the app |
| // and the pointer icon is likely to become stale. Hide it to avoid confusion. |
| InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); |
| } |
| |
| // For purposes of positioning and showing the nav bar, if we have decided that it can't |
| // be hidden (because of the screen aspect ratio), then take that into account. |
| navVisible |= !canHideNavigationBar(); |
| |
| boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf, |
| navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard); |
| if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); |
| updateSysUiVisibility |= layoutStatusBar( |
| displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing); |
| if (updateSysUiVisibility) { |
| updateSystemUiVisibilityLw(); |
| } |
| } |
| layoutScreenDecorWindows(displayFrames, pf, df, dcf); |
| |
| if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { |
| // Make sure that the zone we're avoiding for the cutout is at least as tall as the |
| // status bar; otherwise fullscreen apps will end up cutting halfway into the status |
| // bar. |
| displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, |
| displayFrames.mStable.top); |
| } |
| } |
| |
| private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) { |
| if (mScreenDecorWindows.isEmpty()) { |
| return; |
| } |
| |
| final int displayId = displayFrames.mDisplayId; |
| final Rect dockFrame = displayFrames.mDock; |
| final int displayHeight = displayFrames.mDisplayHeight; |
| final int displayWidth = displayFrames.mDisplayWidth; |
| |
| for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { |
| final WindowState w = mScreenDecorWindows.valueAt(i); |
| if (w.getDisplayId() != displayId || !w.isVisibleLw()) { |
| // Skip if not on the same display or not visible. |
| continue; |
| } |
| |
| w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */, |
| df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */, |
| df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout, |
| false /* parentFrameWasClippedByDisplayCutout */); |
| final Rect frame = w.getFrameLw(); |
| |
| if (frame.left <= 0 && frame.top <= 0) { |
| // Docked at left or top. |
| if (frame.bottom >= displayHeight) { |
| // Docked left. |
| dockFrame.left = Math.max(frame.right, dockFrame.left); |
| } else if (frame.right >= displayWidth ) { |
| // Docked top. |
| dockFrame.top = Math.max(frame.bottom, dockFrame.top); |
| } else { |
| Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w |
| + " not docked on left or top of display. frame=" + frame |
| + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); |
| } |
| } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) { |
| // Docked at right or bottom. |
| if (frame.top <= 0) { |
| // Docked right. |
| dockFrame.right = Math.min(frame.left, dockFrame.right); |
| } else if (frame.left <= 0) { |
| // Docked bottom. |
| dockFrame.bottom = Math.min(frame.top, dockFrame.bottom); |
| } else { |
| Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w |
| + " not docked on right or bottom" + " of display. frame=" + frame |
| + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); |
| } |
| } else { |
| // Screen decor windows are required to be docked on one of the sides of the screen. |
| Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w |
| + " not docked on one of the sides of the display. frame=" + frame |
| + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); |
| } |
| } |
| |
| displayFrames.mRestricted.set(dockFrame); |
| displayFrames.mCurrent.set(dockFrame); |
| displayFrames.mVoiceContent.set(dockFrame); |
| displayFrames.mSystem.set(dockFrame); |
| displayFrames.mContent.set(dockFrame); |
| displayFrames.mRestrictedOverscan.set(dockFrame); |
| } |
| |
| private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf, |
| Rect dcf, int sysui, boolean isKeyguardShowing) { |
| // decide where the status bar goes ahead of time |
| if (mStatusBar == null) { |
| return false; |
| } |
| // apply any navigation bar insets |
| of.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| vf.set(displayFrames.mStable); |
| |
| mStatusBarLayer = mStatusBar.getSurfaceLayer(); |
| |
| // Let the status bar determine its size. |
| mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, |
| vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */, |
| dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */, |
| displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */); |
| |
| // For layout, the status bar is always at the top with our fixed height. |
| displayFrames.mStable.top = displayFrames.mUnrestricted.top |
| + mStatusBarHeightForRotation[displayFrames.mRotation]; |
| // Make sure the status bar covers the entire cutout height |
| displayFrames.mStable.top = Math.max(displayFrames.mStable.top, |
| displayFrames.mDisplayCutoutSafe.top); |
| |
| // Tell the bar controller where the collapsed status bar content is |
| mTmpRect.set(mStatusBar.getContentFrameLw()); |
| mTmpRect.intersect(displayFrames.mDisplayCutoutSafe); |
| mTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset |
| mTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size |
| mStatusBarController.setContentFrame(mTmpRect); |
| |
| boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; |
| boolean statusBarTranslucent = (sysui |
| & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; |
| if (!isKeyguardShowing) { |
| statusBarTranslucent &= areTranslucentBarsAllowed(); |
| } |
| |
| // If the status bar is hidden, we don't want to cause windows behind it to scroll. |
| if (mStatusBar.isVisibleLw() && !statusBarTransient) { |
| // Status bar may go away, so the screen area it occupies is available to apps but just |
| // covering them when the status bar is visible. |
| final Rect dockFrame = displayFrames.mDock; |
| dockFrame.top = displayFrames.mStable.top; |
| displayFrames.mContent.set(dockFrame); |
| displayFrames.mVoiceContent.set(dockFrame); |
| displayFrames.mCurrent.set(dockFrame); |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( |
| "dock=%s content=%s cur=%s", dockFrame.toString(), |
| displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); |
| |
| if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent |
| && !mStatusBarController.wasRecentlyTranslucent()) { |
| // If the opaque status bar is currently requested to be visible, and not in the |
| // process of animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.top = displayFrames.mStable.top; |
| } |
| } |
| return mStatusBarController.checkHiddenLw(); |
| } |
| |
| private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf, |
| boolean navVisible, boolean navTranslucent, boolean navAllowedHidden, |
| boolean statusBarExpandedNotKeyguard) { |
| if (mNavigationBar == null) { |
| return false; |
| } |
| boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); |
| // Force the navigation bar to its appropriate place and size. We need to do this directly, |
| // instead of relying on it to bubble up from the nav bar, because this needs to change |
| // atomically with screen rotations. |
| final int rotation = displayFrames.mRotation; |
| final int displayHeight = displayFrames.mDisplayHeight; |
| final int displayWidth = displayFrames.mDisplayWidth; |
| final Rect dockFrame = displayFrames.mDock; |
| mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); |
| |
| final Rect cutoutSafeUnrestricted = mTmpRect; |
| cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); |
| cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); |
| |
| if (mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| // It's a system nav bar or a portrait screen; nav bar goes on bottom. |
| final int top = cutoutSafeUnrestricted.bottom |
| - getNavigationBarHeight(rotation, uiMode); |
| mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom); |
| displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; |
| if (transientNavBarShowing) { |
| mNavigationBarController.setBarShowingLw(true); |
| } else if (navVisible) { |
| mNavigationBarController.setBarShowingLw(true); |
| dockFrame.bottom = displayFrames.mRestricted.bottom |
| = displayFrames.mRestrictedOverscan.bottom = top; |
| } else { |
| // We currently want to hide the navigation UI - unless we expanded the status bar. |
| mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); |
| } |
| if (navVisible && !navTranslucent && !navAllowedHidden |
| && !mNavigationBar.isAnimatingLw() |
| && !mNavigationBarController.wasRecentlyTranslucent()) { |
| // If the opaque nav bar is currently requested to be visible and not in the process |
| // of animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.bottom = top; |
| } |
| } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { |
| // Landscape screen; nav bar goes to the right. |
| final int left = cutoutSafeUnrestricted.right |
| - getNavigationBarWidth(rotation, uiMode); |
| mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); |
| displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; |
| if (transientNavBarShowing) { |
| mNavigationBarController.setBarShowingLw(true); |
| } else if (navVisible) { |
| mNavigationBarController.setBarShowingLw(true); |
| dockFrame.right = displayFrames.mRestricted.right |
| = displayFrames.mRestrictedOverscan.right = left; |
| } else { |
| // We currently want to hide the navigation UI - unless we expanded the status bar. |
| mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); |
| } |
| if (navVisible && !navTranslucent && !navAllowedHidden |
| && !mNavigationBar.isAnimatingLw() |
| && !mNavigationBarController.wasRecentlyTranslucent()) { |
| // If the nav bar is currently requested to be visible, and not in the process of |
| // animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.right = left; |
| } |
| } else if (mNavigationBarPosition == NAV_BAR_LEFT) { |
| // Seascape screen; nav bar goes to the left. |
| final int right = cutoutSafeUnrestricted.left |
| + getNavigationBarWidth(rotation, uiMode); |
| mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); |
| displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; |
| if (transientNavBarShowing) { |
| mNavigationBarController.setBarShowingLw(true); |
| } else if (navVisible) { |
| mNavigationBarController.setBarShowingLw(true); |
| dockFrame.left = displayFrames.mRestricted.left = |
| displayFrames.mRestrictedOverscan.left = right; |
| } else { |
| // We currently want to hide the navigation UI - unless we expanded the status bar. |
| mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard); |
| } |
| if (navVisible && !navTranslucent && !navAllowedHidden |
| && !mNavigationBar.isAnimatingLw() |
| && !mNavigationBarController.wasRecentlyTranslucent()) { |
| // If the nav bar is currently requested to be visible, and not in the process of |
| // animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.left = right; |
| } |
| } |
| |
| // Make sure the content and current rectangles are updated to account for the restrictions |
| // from the navigation bar. |
| displayFrames.mCurrent.set(dockFrame); |
| displayFrames.mVoiceContent.set(dockFrame); |
| displayFrames.mContent.set(dockFrame); |
| mStatusBarLayer = mNavigationBar.getSurfaceLayer(); |
| // And compute the final frame. |
| mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame, |
| mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf, |
| mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, |
| displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */); |
| mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw()); |
| |
| if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame); |
| return mNavigationBarController.checkHiddenLw(); |
| } |
| |
| @NavigationBarPosition |
| private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { |
| if (mNavigationBarCanMove && displayWidth > displayHeight) { |
| if (displayRotation == Surface.ROTATION_270) { |
| return NAV_BAR_LEFT; |
| } else { |
| return NAV_BAR_RIGHT; |
| } |
| } |
| return NAV_BAR_BOTTOM; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int getSystemDecorLayerLw() { |
| if (mStatusBar != null && mStatusBar.isVisibleLw()) { |
| return mStatusBar.getSurfaceLayer(); |
| } |
| |
| if (mNavigationBar != null && mNavigationBar.isVisibleLw()) { |
| return mNavigationBar.getSurfaceLayer(); |
| } |
| |
| return 0; |
| } |
| |
| private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, |
| boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf, |
| DisplayFrames displayFrames) { |
| if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) { |
| // Here's a special case: if the child window is not the 'dock window' |
| // or input method target, and the window it is attached to is below |
| // the dock window, then the frames we computed for the window it is |
| // attached to can not be used because the dock is effectively part |
| // of the underlying window and the attached window is floating on top |
| // of the whole thing. So, we ignore the attached window and explicitly |
| // compute the frames that would be appropriate without the dock. |
| vf.set(displayFrames.mDock); |
| cf.set(displayFrames.mDock); |
| of.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| } else { |
| // The effective display frame of the attached window depends on whether it is taking |
| // care of insetting its content. If not, we need to use the parent's content frame so |
| // that the entire window is positioned within that content. Otherwise we can use the |
| // overscan frame and let the attached window take care of positioning its content |
| // appropriately. |
| if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| // Set the content frame of the attached window to the parent's decor frame |
| // (same as content frame when IME isn't present) if specifically requested by |
| // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. |
| // Otherwise, use the overscan frame. |
| cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 |
| ? attached.getContentFrameLw() : attached.getOverscanFrameLw()); |
| } else { |
| // If the window is resizing, then we want to base the content frame on our attached |
| // content frame to resize...however, things can be tricky if the attached window is |
| // NOT in resize mode, in which case its content frame will be larger. |
| // Ungh. So to deal with that, make sure the content frame we end up using is not |
| // covering the IM dock. |
| cf.set(attached.getContentFrameLw()); |
| if (attached.isVoiceInteraction()) { |
| cf.intersectUnchecked(displayFrames.mVoiceContent); |
| } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { |
| cf.intersectUnchecked(displayFrames.mContent); |
| } |
| } |
| df.set(insetDecors ? attached.getDisplayFrameLw() : cf); |
| of.set(insetDecors ? attached.getOverscanFrameLw() : cf); |
| vf.set(attached.getVisibleFrameLw()); |
| } |
| // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be |
| // positioned relative to its parent or the entire screen. |
| pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); |
| } |
| |
| private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { |
| if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { |
| return; |
| } |
| // If app is requesting a stable layout, don't let the content insets go below the stable |
| // values. |
| if ((fl & FLAG_FULLSCREEN) != 0) { |
| r.intersectUnchecked(displayFrames.mStableFullscreen); |
| } else { |
| r.intersectUnchecked(displayFrames.mStable); |
| } |
| } |
| |
| private boolean canReceiveInput(WindowState win) { |
| boolean notFocusable = |
| (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; |
| boolean altFocusableIm = |
| (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0; |
| boolean notFocusableForIm = notFocusable ^ altFocusableIm; |
| return !notFocusableForIm; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { |
| // We've already done the navigation bar, status bar, and all screen decor windows. If the |
| // status bar can receive input, we need to layout it again to accommodate for the IME |
| // window. |
| if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar |
| || mScreenDecorWindows.contains(win)) { |
| return; |
| } |
| final WindowManager.LayoutParams attrs = win.getAttrs(); |
| final boolean isDefaultDisplay = win.isDefaultDisplay(); |
| final boolean needsToOffsetInputMethodTarget = isDefaultDisplay && |
| (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null); |
| if (needsToOffsetInputMethodTarget) { |
| if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state"); |
| offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames); |
| } |
| |
| final int type = attrs.type; |
| final int fl = PolicyControl.getWindowFlags(win, attrs); |
| final int pfl = attrs.privateFlags; |
| final int sim = attrs.softInputMode; |
| final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); |
| final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); |
| |
| final Rect pf = mTmpParentFrame; |
| final Rect df = mTmpDisplayFrame; |
| final Rect of = mTmpOverscanFrame; |
| final Rect cf = mTmpContentFrame; |
| final Rect vf = mTmpVisibleFrame; |
| final Rect dcf = mTmpDecorFrame; |
| final Rect sf = mTmpStableFrame; |
| Rect osf = null; |
| dcf.setEmpty(); |
| |
| final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar |
| && mNavigationBar != null && mNavigationBar.isVisibleLw()); |
| |
| final int adjust = sim & SOFT_INPUT_MASK_ADJUST; |
| |
| final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 |
| || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; |
| |
| final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; |
| final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; |
| |
| sf.set(displayFrames.mStable); |
| |
| if (type == TYPE_INPUT_METHOD) { |
| vf.set(displayFrames.mDock); |
| cf.set(displayFrames.mDock); |
| of.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| pf.set(displayFrames.mDock); |
| // IM dock windows layout below the nav bar... |
| pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; |
| // ...with content insets above the nav bar |
| cf.bottom = vf.bottom = displayFrames.mStable.bottom; |
| if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { |
| // The status bar forces the navigation bar while it's visible. Make sure the IME |
| // avoids the navigation bar in that case. |
| if (mNavigationBarPosition == NAV_BAR_RIGHT) { |
| pf.right = df.right = of.right = cf.right = vf.right = |
| displayFrames.mStable.right; |
| } else if (mNavigationBarPosition == NAV_BAR_LEFT) { |
| pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left; |
| } |
| } |
| // IM dock windows always go to the bottom of the screen. |
| attrs.gravity = Gravity.BOTTOM; |
| mDockLayer = win.getSurfaceLayer(); |
| } else if (type == TYPE_VOICE_INTERACTION) { |
| of.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| } |
| if (adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } else if (type == TYPE_WALLPAPER) { |
| layoutWallpaper(displayFrames, pf, df, of, cf); |
| } else if (win == mStatusBar) { |
| of.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| cf.set(displayFrames.mStable); |
| vf.set(displayFrames.mStable); |
| |
| if (adjust == SOFT_INPUT_ADJUST_RESIZE) { |
| cf.bottom = displayFrames.mContent.bottom; |
| } else { |
| cf.bottom = displayFrames.mDock.bottom; |
| vf.bottom = displayFrames.mContent.bottom; |
| } |
| } else { |
| dcf.set(displayFrames.mSystem); |
| final boolean inheritTranslucentDecor = |
| (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0; |
| final boolean isAppWindow = |
| type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; |
| final boolean topAtRest = |
| win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); |
| if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { |
| if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 |
| && (fl & FLAG_FULLSCREEN) == 0 |
| && (fl & FLAG_TRANSLUCENT_STATUS) == 0 |
| && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 |
| && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) { |
| // Ensure policy decor includes status bar |
| dcf.top = displayFrames.mStable.top; |
| } |
| if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 |
| && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 |
| && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { |
| // Ensure policy decor includes navigation bar |
| dcf.bottom = displayFrames.mStable.bottom; |
| dcf.right = displayFrames.mStable.right; |
| } |
| } |
| |
| if (layoutInScreen && layoutInsetDecor) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): IN_SCREEN, INSET_DECOR"); |
| // This is the case for a normal activity window: we want it to cover all of the |
| // screen space, and it can take care of moving its contents to account for screen |
| // decorations that intrude into that space. |
| if (attached != null) { |
| // If this window is attached to another, our display |
| // frame is the same as the one we are attached to. |
| setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf, |
| displayFrames); |
| } else { |
| if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { |
| // Status bar panels are the only windows who can go on top of the status |
| // bar. They are protected by the STATUS_BAR_SERVICE permission, so they |
| // have the same privileges as the status bar itself. |
| // |
| // However, they should still dodge the navigation bar if it exists. |
| |
| pf.left = df.left = of.left = hasNavBar |
| ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; |
| pf.top = df.top = of.top = displayFrames.mUnrestricted.top; |
| pf.right = df.right = of.right = hasNavBar |
| ? displayFrames.mRestricted.right |
| : displayFrames.mUnrestricted.right; |
| pf.bottom = df.bottom = of.bottom = hasNavBar |
| ? displayFrames.mRestricted.bottom |
| : displayFrames.mUnrestricted.bottom; |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG, String.format( |
| "Laying out status bar window: (%d,%d - %d,%d)", |
| pf.left, pf.top, pf.right, pf.bottom)); |
| } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 |
| && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { |
| // Asking to layout into the overscan region, so give it that pure |
| // unrestricted area. |
| of.set(displayFrames.mOverscan); |
| df.set(displayFrames.mOverscan); |
| pf.set(displayFrames.mOverscan); |
| } else if (canHideNavigationBar() |
| && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 |
| && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW |
| || type == TYPE_VOLUME_OVERLAY)) { |
| // Asking for layout as if the nav bar is hidden, lets the application |
| // extend into the unrestricted overscan screen area. We only do this for |
| // application windows and certain system windows to ensure no window that |
| // can be above the nav bar can do this. |
| df.set(displayFrames.mOverscan); |
| pf.set(displayFrames.mOverscan); |
| // We need to tell the app about where the frame inside the overscan is, so |
| // it can inset its content by that amount -- it didn't ask to actually |
| // extend itself into the overscan region. |
| of.set(displayFrames.mUnrestricted); |
| } else { |
| df.set(displayFrames.mRestrictedOverscan); |
| pf.set(displayFrames.mRestrictedOverscan); |
| // We need to tell the app about where the frame inside the overscan |
| // is, so it can inset its content by that amount -- it didn't ask |
| // to actually extend itself into the overscan region. |
| of.set(displayFrames.mUnrestricted); |
| } |
| |
| if ((fl & FLAG_FULLSCREEN) == 0) { |
| if (win.isVoiceInteraction()) { |
| cf.set(displayFrames.mVoiceContent); |
| } else { |
| if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| } |
| } |
| } else { |
| // Full screen windows are always given a layout that is as if the status |
| // bar and other transient decors are gone. This is to avoid bad states when |
| // moving from a window that is not hiding the status bar to one that is. |
| cf.set(displayFrames.mRestricted); |
| } |
| applyStableConstraints(sysUiFl, fl, cf, displayFrames); |
| if (adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } |
| } else if (layoutInScreen || (sysUiFl |
| & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): IN_SCREEN"); |
| // A window that has requested to fill the entire screen just |
| // gets everything, period. |
| if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { |
| cf.set(displayFrames.mUnrestricted); |
| of.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| if (hasNavBar) { |
| pf.left = df.left = of.left = cf.left = displayFrames.mDock.left; |
| pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right; |
| pf.bottom = df.bottom = of.bottom = cf.bottom = |
| displayFrames.mRestricted.bottom; |
| } |
| if (DEBUG_LAYOUT) Slog.v(TAG, String.format( |
| "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)", |
| pf.left, pf.top, pf.right, pf.bottom)); |
| } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { |
| // The navigation bar has Real Ultimate Power. |
| of.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| if (DEBUG_LAYOUT) Slog.v(TAG, String.format( |
| "Laying out navigation bar window: (%d,%d - %d,%d)", |
| pf.left, pf.top, pf.right, pf.bottom)); |
| } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) |
| && ((fl & FLAG_FULLSCREEN) != 0)) { |
| // Fullscreen secure system overlays get what they ask for. Screenshot region |
| // selection overlay should also expand to full screen. |
| cf.set(displayFrames.mOverscan); |
| of.set(displayFrames.mOverscan); |
| df.set(displayFrames.mOverscan); |
| pf.set(displayFrames.mOverscan); |
| } else if (type == TYPE_BOOT_PROGRESS) { |
| // Boot progress screen always covers entire display. |
| cf.set(displayFrames.mOverscan); |
| of.set(displayFrames.mOverscan); |
| df.set(displayFrames.mOverscan); |
| pf.set(displayFrames.mOverscan); |
| } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 |
| && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { |
| // Asking to layout into the overscan region, so give it that pure unrestricted |
| // area. |
| cf.set(displayFrames.mOverscan); |
| of.set(displayFrames.mOverscan); |
| df.set(displayFrames.mOverscan); |
| pf.set(displayFrames.mOverscan); |
| } else if (canHideNavigationBar() |
| && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 |
| && (type == TYPE_STATUS_BAR |
| || type == TYPE_TOAST |
| || type == TYPE_DOCK_DIVIDER |
| || type == TYPE_VOICE_INTERACTION_STARTING |
| || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { |
| // Asking for layout as if the nav bar is hidden, lets the |
| // application extend into the unrestricted screen area. We |
| // only do this for application windows (or toasts) to ensure no window that |
| // can be above the nav bar can do this. |
| // XXX This assumes that an app asking for this will also |
| // ask for layout in only content. We can't currently figure out |
| // what the screen would be if only laying out to hide the nav bar. |
| cf.set(displayFrames.mUnrestricted); |
| of.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { |
| of.set(displayFrames.mRestricted); |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| } |
| } else { |
| cf.set(displayFrames.mRestricted); |
| of.set(displayFrames.mRestricted); |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| } |
| |
| applyStableConstraints(sysUiFl, fl, cf,displayFrames); |
| |
| if (adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } else if (attached != null) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): attached to " + attached); |
| // A child window should be placed inside of the same visible |
| // frame that its parent had. |
| setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf, |
| displayFrames); |
| } else { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + |
| "): normal window"); |
| // Otherwise, a normal window must be placed inside the content |
| // of all screen decorations. |
| if (type == TYPE_STATUS_BAR_PANEL) { |
| // Status bar panels can go on |
| // top of the status bar. They are protected by the STATUS_BAR_SERVICE |
| // permission, so they have the same privileges as the status bar itself. |
| cf.set(displayFrames.mRestricted); |
| of.set(displayFrames.mRestricted); |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { |
| // These dialogs are stable to interim decor changes. |
| cf.set(displayFrames.mStable); |
| of.set(displayFrames.mStable); |
| df.set(displayFrames.mStable); |
| pf.set(displayFrames.mStable); |
| } else { |
| pf.set(displayFrames.mContent); |
| if (win.isVoiceInteraction()) { |
| cf.set(displayFrames.mVoiceContent); |
| of.set(displayFrames.mVoiceContent); |
| df.set(displayFrames.mVoiceContent); |
| } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| of.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| of.set(displayFrames.mContent); |
| df.set(displayFrames.mContent); |
| } |
| if (adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } |
| } |
| } |
| |
| boolean parentFrameWasClippedByDisplayCutout = false; |
| final int cutoutMode = attrs.layoutInDisplayCutoutMode; |
| final boolean attachedInParent = attached != null && !layoutInScreen; |
| final boolean requestedHideNavigation = |
| (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; |
| |
| // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get |
| // cropped / shifted to the displayFrame in WindowState. |
| final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen |
| && type != TYPE_BASE_APPLICATION; |
| |
| // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in |
| // the cutout safe zone. |
| if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { |
| final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect; |
| displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); |
| if (layoutInScreen && layoutInsetDecor && !requestedFullscreen |
| && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { |
| // At the top we have the status bar, so apps that are |
| // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN |
| // already expect that there's an inset there and we don't need to exclude |
| // the window from that area. |
| displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; |
| } |
| if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation |
| && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { |
| // Same for the navigation bar. |
| switch (mNavigationBarPosition) { |
| case NAV_BAR_BOTTOM: |
| displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; |
| break; |
| case NAV_BAR_RIGHT: |
| displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; |
| break; |
| case NAV_BAR_LEFT: |
| displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; |
| break; |
| } |
| } |
| if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| // The IME can always extend under the bottom cutout if the navbar is there. |
| displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; |
| } |
| // Windows that are attached to a parent and laid out in said parent already avoid |
| // the cutout according to that parent and don't need to be further constrained. |
| // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. |
| // They will later be cropped or shifted using the displayFrame in WindowState, |
| // which prevents overlap with the DisplayCutout. |
| if (!attachedInParent && !floatingInScreenWindow) { |
| mTmpRect.set(pf); |
| pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); |
| parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf); |
| } |
| // Make sure that NO_LIMITS windows clipped to the display don't extend under the |
| // cutout. |
| df.intersectUnchecked(displayCutoutSafeExceptMaybeBars); |
| } |
| |
| // Content should never appear in the cutout. |
| cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); |
| |
| // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. |
| // Also, we don't allow windows in multi-window mode to extend out of the screen. |
| if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR |
| && !win.isInMultiWindowMode()) { |
| df.left = df.top = -10000; |
| df.right = df.bottom = 10000; |
| if (type != TYPE_WALLPAPER) { |
| of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; |
| of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000; |
| } |
| } |
| |
| // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we |
| // need to provide information to the clients that want to pretend that you can draw there. |
| // We only want to apply outsets to certain types of windows. For example, we never want to |
| // apply the outsets to floating dialogs, because they wouldn't make sense there. |
| final boolean useOutsets = shouldUseOutsets(attrs, fl); |
| if (isDefaultDisplay && useOutsets) { |
| osf = mTmpOutsetFrame; |
| osf.set(cf.left, cf.top, cf.right, cf.bottom); |
| int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); |
| if (outset > 0) { |
| int rotation = displayFrames.mRotation; |
| if (rotation == Surface.ROTATION_0) { |
| osf.bottom += outset; |
| } else if (rotation == Surface.ROTATION_90) { |
| osf.right += outset; |
| } else if (rotation == Surface.ROTATION_180) { |
| osf.top -= outset; |
| } else if (rotation == Surface.ROTATION_270) { |
| osf.left -= outset; |
| } |
| if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset |
| + " with rotation " + rotation + ", result: " + osf); |
| } |
| } |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() |
| + ": sim=#" + Integer.toHexString(sim) |
| + " attach=" + attached + " type=" + type |
| + String.format(" flags=0x%08x", fl) |
| + " pf=" + pf.toShortString() + " df=" + df.toShortString() |
| + " of=" + of.toShortString() |
| + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() |
| + " dcf=" + dcf.toShortString() |
| + " sf=" + sf.toShortString() |
| + " osf=" + (osf == null ? "null" : osf.toShortString())); |
| |
| win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout, |
| parentFrameWasClippedByDisplayCutout); |
| // Dock windows carve out the bottom of the screen, so normal windows |
| // can't appear underneath them. |
| if (type == TYPE_INPUT_METHOD && win.isVisibleLw() |
| && !win.getGivenInsetsPendingLw()) { |
| setLastInputMethodWindowLw(null, null); |
| offsetInputMethodWindowLw(win, displayFrames); |
| } |
| if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() |
| && !win.getGivenInsetsPendingLw()) { |
| offsetVoiceInputWindowLw(win, displayFrames); |
| } |
| } |
| |
| private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) { |
| // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area. |
| df.set(displayFrames.mOverscan); |
| pf.set(displayFrames.mOverscan); |
| cf.set(displayFrames.mUnrestricted); |
| of.set(displayFrames.mUnrestricted); |
| } |
| |
| private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { |
| int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); |
| top += win.getGivenContentInsetsLw().top; |
| displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); |
| displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); |
| top = win.getVisibleFrameLw().top; |
| top += win.getGivenVisibleInsetsLw().top; |
| displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" |
| + displayFrames.mDock.bottom + " mContentBottom=" |
| + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom); |
| } |
| |
| private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { |
| int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); |
| top += win.getGivenContentInsetsLw().top; |
| displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { |
| mTopFullscreenOpaqueWindowState = null; |
| mTopFullscreenOpaqueOrDimmingWindowState = null; |
| mTopDockedOpaqueWindowState = null; |
| mTopDockedOpaqueOrDimmingWindowState = null; |
| mForceStatusBar = false; |
| mForceStatusBarFromKeyguard = false; |
| mForceStatusBarTransparent = false; |
| mForcingShowNavBar = false; |
| mForcingShowNavBarLayer = -1; |
| |
| mAllowLockscreenWhenOn = false; |
| mShowingDream = false; |
| mWindowSleepTokenNeeded = false; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, |
| WindowState attached, WindowState imeTarget) { |
| final boolean affectsSystemUi = win.canAffectSystemUiFlags(); |
| if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); |
| applyKeyguardPolicyLw(win, imeTarget); |
| final int fl = PolicyControl.getWindowFlags(win, attrs); |
| if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi |
| && attrs.type == TYPE_INPUT_METHOD) { |
| mForcingShowNavBar = true; |
| mForcingShowNavBarLayer = win.getSurfaceLayer(); |
| } |
| if (attrs.type == TYPE_STATUS_BAR) { |
| if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { |
| mForceStatusBarFromKeyguard = true; |
| } |
| if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { |
| mForceStatusBarTransparent = true; |
| } |
| } |
| |
| boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW |
| && attrs.type < FIRST_SYSTEM_WINDOW; |
| final int windowingMode = win.getWindowingMode(); |
| final boolean inFullScreenOrSplitScreenSecondaryWindowingMode = |
| windowingMode == WINDOWING_MODE_FULLSCREEN |
| || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; |
| if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) { |
| if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { |
| mForceStatusBar = true; |
| } |
| if (attrs.type == TYPE_DREAM) { |
| // If the lockscreen was showing when the dream started then wait |
| // for the dream to draw before hiding the lockscreen. |
| if (!mDreamingLockscreen |
| || (win.isVisibleLw() && win.hasDrawnLw())) { |
| mShowingDream = true; |
| appWindow = true; |
| } |
| } |
| |
| // For app windows that are not attached, we decide if all windows in the app they |
| // represent should be hidden or if we should hide the lockscreen. For attached app |
| // windows we defer the decision to the window it is attached to. |
| if (appWindow && attached == null) { |
| if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); |
| mTopFullscreenOpaqueWindowState = win; |
| if (mTopFullscreenOpaqueOrDimmingWindowState == null) { |
| mTopFullscreenOpaqueOrDimmingWindowState = win; |
| } |
| if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { |
| mAllowLockscreenWhenOn = true; |
| } |
| } |
| } |
| } |
| |
| // Voice interaction overrides both top fullscreen and top docked. |
| if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) { |
| if (mTopFullscreenOpaqueWindowState == null) { |
| mTopFullscreenOpaqueWindowState = win; |
| if (mTopFullscreenOpaqueOrDimmingWindowState == null) { |
| mTopFullscreenOpaqueOrDimmingWindowState = win; |
| } |
| } |
| if (mTopDockedOpaqueWindowState == null) { |
| mTopDockedOpaqueWindowState = win; |
| if (mTopDockedOpaqueOrDimmingWindowState == null) { |
| mTopDockedOpaqueOrDimmingWindowState = win; |
| } |
| } |
| } |
| |
| // Keep track of the window if it's dimming but not necessarily fullscreen. |
| if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi |
| && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) { |
| mTopFullscreenOpaqueOrDimmingWindowState = win; |
| } |
| |
| // We need to keep track of the top "fullscreen" opaque window for the docked stack |
| // separately, because both the "real fullscreen" opaque window and the one for the docked |
| // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. |
| if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null |
| && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { |
| mTopDockedOpaqueWindowState = win; |
| if (mTopDockedOpaqueOrDimmingWindowState == null) { |
| mTopDockedOpaqueOrDimmingWindowState = win; |
| } |
| } |
| |
| // Also keep track of any windows that are dimming but not necessarily fullscreen in the |
| // docked stack. |
| if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() |
| && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { |
| mTopDockedOpaqueOrDimmingWindowState = win; |
| } |
| |
| // Take note if a window wants to acquire a sleep token. |
| if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0 |
| && win.canAcquireSleepToken()) { |
| mWindowSleepTokenNeeded = true; |
| } |
| } |
| |
| private void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) { |
| if (canBeHiddenByKeyguardLw(win)) { |
| if (shouldBeHiddenByKeyguard(win, imeTarget)) { |
| win.hideLw(false /* doAnimation */); |
| } else { |
| win.showLw(false /* doAnimation */); |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int finishPostLayoutPolicyLw() { |
| int changes = 0; |
| boolean topIsFullscreen = false; |
| |
| final WindowManager.LayoutParams lp = (mTopFullscreenOpaqueWindowState != null) |
| ? mTopFullscreenOpaqueWindowState.getAttrs() |
| : null; |
| |
| // If we are not currently showing a dream then remember the current |
| // lockscreen state. We will use this to determine whether the dream |
| // started while the lockscreen was showing and remember this state |
| // while the dream is showing. |
| if (!mShowingDream) { |
| mDreamingLockscreen = isKeyguardShowingAndNotOccluded(); |
| if (mDreamingSleepTokenNeeded) { |
| mDreamingSleepTokenNeeded = false; |
| mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget(); |
| } |
| } else { |
| if (!mDreamingSleepTokenNeeded) { |
| mDreamingSleepTokenNeeded = true; |
| mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget(); |
| } |
| } |
| |
| if (mStatusBar != null) { |
| if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar |
| + " forcefkg=" + mForceStatusBarFromKeyguard |
| + " top=" + mTopFullscreenOpaqueWindowState); |
| boolean shouldBeTransparent = mForceStatusBarTransparent |
| && !mForceStatusBar |
| && !mForceStatusBarFromKeyguard; |
| if (!shouldBeTransparent) { |
| mStatusBarController.setShowTransparent(false /* transparent */); |
| } else if (!mStatusBar.isVisibleLw()) { |
| mStatusBarController.setShowTransparent(true /* transparent */); |
| } |
| |
| boolean statusBarExpanded = |
| (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0; |
| boolean topAppHidesStatusBar = topAppHidesStatusBar(); |
| if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent |
| || statusBarExpanded) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); |
| if (mStatusBarController.setBarShowingLw(true)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| // Maintain fullscreen layout until incoming animation is complete. |
| topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw(); |
| // Transient status bar on the lockscreen is not allowed |
| if ((mForceStatusBarFromKeyguard || statusBarExpanded) |
| && mStatusBarController.isTransientShowing()) { |
| mStatusBarController.updateVisibilityLw(false /*transientAllowed*/, |
| mLastSystemUiFlags, mLastSystemUiFlags); |
| } |
| } else if (mTopFullscreenOpaqueWindowState != null) { |
| topIsFullscreen = topAppHidesStatusBar; |
| // The subtle difference between the window for mTopFullscreenOpaqueWindowState |
| // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window |
| // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the |
| // case though. |
| if (mStatusBarController.isTransientShowing()) { |
| if (mStatusBarController.setBarShowingLw(true)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| } else if (topIsFullscreen |
| && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM) |
| && !mWindowManagerInternal.isStackVisible( |
| WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar"); |
| if (mStatusBarController.setBarShowingLw(false)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } else { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding"); |
| } |
| } else { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen"); |
| if (mStatusBarController.setBarShowingLw(true)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| topAppHidesStatusBar = false; |
| } |
| } |
| mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar); |
| } |
| |
| if (mTopIsFullscreen != topIsFullscreen) { |
| if (!topIsFullscreen) { |
| // Force another layout when status bar becomes fully shown. |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| mTopIsFullscreen = topIsFullscreen; |
| } |
| |
| if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) { |
| // If the navigation bar has been hidden or shown, we need to do another |
| // layout pass to update that window. |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| |
| if (mShowingDream != mLastShowingDream) { |
| mLastShowingDream = mShowingDream; |
| mWindowManagerFuncs.notifyShowingDreamChanged(); |
| } |
| |
| updateWindowSleepToken(); |
| |
| // update since mAllowLockscreenWhenOn might have changed |
| updateLockScreenTimeout(); |
| return changes; |
| } |
| |
| private void updateWindowSleepToken() { |
| if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) { |
| mHandler.removeCallbacks(mReleaseSleepTokenRunnable); |
| mHandler.post(mAcquireSleepTokenRunnable); |
| } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) { |
| mHandler.removeCallbacks(mAcquireSleepTokenRunnable); |
| mHandler.post(mReleaseSleepTokenRunnable); |
| } |
| mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded; |
| } |
| |
| /** |
| * @return Whether the top app should hide the statusbar based on the top fullscreen opaque |
| * window. |
| */ |
| private boolean topAppHidesStatusBar() { |
| if (mTopFullscreenOpaqueWindowState == null) { |
| return false; |
| } |
| final int fl = PolicyControl.getWindowFlags(null, |
| mTopFullscreenOpaqueWindowState.getAttrs()); |
| if (localLOGV) { |
| Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); |
| Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() |
| + " lp.flags=0x" + Integer.toHexString(fl)); |
| } |
| return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 |
| || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; |
| } |
| |
| /** |
| * Updates the occluded state of the Keyguard. |
| * |
| * @return Whether the flags have changed and we have to redo the layout. |
| */ |
| private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force) { |
| if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded); |
| final boolean wasOccluded = mKeyguardOccluded; |
| final boolean showing = mKeyguardDelegate.isShowing(); |
| final boolean changed = wasOccluded != isOccluded || force; |
| if (!isOccluded && changed && showing) { |
| mKeyguardOccluded = false; |
| mKeyguardDelegate.setOccluded(false, true /* animate */); |
| if (mStatusBar != null) { |
| mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; |
| if (!mKeyguardDelegate.hasLockscreenWallpaper()) { |
| mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER; |
| } |
| } |
| return true; |
| } else if (isOccluded && changed && showing) { |
| mKeyguardOccluded = true; |
| mKeyguardDelegate.setOccluded(true, false /* animate */); |
| if (mStatusBar != null) { |
| mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD; |
| mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; |
| } |
| return true; |
| } else if (changed) { |
| mKeyguardOccluded = isOccluded; |
| mKeyguardDelegate.setOccluded(isOccluded, false /* animate */); |
| return false; |
| } else { |
| return false; |
| } |
| } |
| |
| private boolean isStatusBarKeyguard() { |
| return mStatusBar != null |
| && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; |
| } |
| |
| @Override |
| public boolean allowAppAnimationsLw() { |
| return !mShowingDream; |
| } |
| |
| @Override |
| public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { |
| mFocusedWindow = newFocus; |
| if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) { |
| // If the navigation bar has been hidden or shown, we need to do another |
| // layout pass to update that window. |
| return FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| return 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { |
| // lid changed state |
| final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED; |
| if (newLidState == mLidState) { |
| return; |
| } |
| |
| mLidState = newLidState; |
| applyLidSwitchState(); |
| updateRotation(true); |
| |
| if (lidOpen) { |
| wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch, |
| "android.policy:LID"); |
| } else if (!mLidControlsSleep) { |
| 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 (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, |
| "android.policy:CAMERA_COVER"); |
| startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); |
| } |
| mCameraLensCoverState = lensCoverState; |
| } |
| |
| void setHdmiPlugged(boolean plugged) { |
| if (mHdmiPlugged != plugged) { |
| mHdmiPlugged = plugged; |
| updateRotation(true, true); |
| Intent intent = new Intent(ACTION_HDMI_PLUGGED); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged); |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| } |
| } |
| |
| 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) { |
| } |
| } |
| } |
| } |
| // 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. |
| mHdmiPlugged = !plugged; |
| setHdmiPlugged(!mHdmiPlugged); |
| } |
| |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { |
| if (!mSystemBooted) { |
| // If we have not yet booted, don't let key events do anything. |
| return 0; |
| } |
| |
| final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0; |
| final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; |
| final boolean canceled = event.isCanceled(); |
| final int keyCode = event.getKeyCode(); |
| |
| final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; |
| |
| // 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 ? false : |
| (interactive ? |
| isKeyguardShowingAndNotOccluded() : |
| mKeyguardDelegate.isShowing())); |
| |
| if (DEBUG_INPUT) { |
| Log.d(TAG, "interceptKeyTq keycode=" + keyCode |
| + " interactive=" + interactive + " keyguardActive=" + keyguardActive |
| + " policyFlags=" + Integer.toHexString(policyFlags)); |
| } |
| |
| // Basic policy based on interactive state. |
| int result; |
| boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 |
| || event.isWakeKey(); |
| 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 (!interactive && shouldDispatchInputWhenNonInteractive(event)) { |
| // 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, event)) { |
| if (isWakeKey) { |
| wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY"); |
| } |
| return result; |
| } |
| |
| // 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: { |
| if (down) { |
| interceptBackKeyDown(); |
| } else { |
| boolean handled = interceptBackKeyUp(event); |
| |
| // Don't pass back press to app if we've already handled it via long press |
| if (handled) { |
| result &= ~ACTION_PASS_TO_USER; |
| } |
| } |
| break; |
| } |
| |
| case KeyEvent.KEYCODE_VOLUME_DOWN: |
| case KeyEvent.KEYCODE_VOLUME_UP: |
| case KeyEvent.KEYCODE_VOLUME_MUTE: { |
| if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { |
| if (down) { |
| // Any activity on the vol down button stops the ringer toggle shortcut |
| cancelPendingRingerToggleChordAction(); |
| |
| if (interactive && !mScreenshotChordVolumeDownKeyTriggered |
| && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { |
| mScreenshotChordVolumeDownKeyTriggered = true; |
| mScreenshotChordVolumeDownKeyTime = event.getDownTime(); |
| mScreenshotChordVolumeDownKeyConsumed = false; |
| cancelPendingPowerKeyAction(); |
| interceptScreenshotChord(); |
| interceptAccessibilityShortcutChord(); |
| } |
| } else { |
| mScreenshotChordVolumeDownKeyTriggered = false; |
| cancelPendingScreenshotChordAction(); |
| cancelPendingAccessibilityShortcutAction(); |
| } |
| } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { |
| if (down) { |
| if (interactive && !mA11yShortcutChordVolumeUpKeyTriggered |
| && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { |
| mA11yShortcutChordVolumeUpKeyTriggered = true; |
| mA11yShortcutChordVolumeUpKeyTime = event.getDownTime(); |
| mA11yShortcutChordVolumeUpKeyConsumed = false; |
| cancelPendingPowerKeyAction(); |
| cancelPendingScreenshotChordAction(); |
| cancelPendingRingerToggleChordAction(); |
| |
| interceptAccessibilityShortcutChord(); |
| interceptRingerToggleChord(); |
| } |
| } else { |
| mA11yShortcutChordVolumeUpKeyTriggered = false; |
| cancelPendingScreenshotChordAction(); |
| cancelPendingAccessibilityShortcutAction(); |
| cancelPendingRingerToggleChordAction(); |
| } |
| } |
| if (down) { |
| sendSystemKeyToStatusBarAsync(event.getKeyCode()); |
| |
| 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) { |
| goToSleep(event.getEventTime(), |
| PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); |
| isWakeKey = false; |
| } |
| } |
| } |
| } |
| break; |
| } |
| |
| case KeyEvent.KEYCODE_POWER: { |
| // Any activity on the power button stops the accessibility shortcut |
| cancelPendingAccessibilityShortcutAction(); |
| result &= ~ACTION_PASS_TO_USER; |
| isWakeKey = false; // wake-up will be handled separately |
| if (down) { |
| interceptPowerKeyDown(event, interactive); |
| } else { |
| interceptPowerKeyUp(event, interactive, 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: { |
| result &= ~ACTION_PASS_TO_USER; |
| interceptSystemNavigationKey(event); |
| break; |
| } |
| |
| case KeyEvent.KEYCODE_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()); |
| } |
| break; |
| } |
| |
| case KeyEvent.KEYCODE_SOFT_SLEEP: { |
| result &= ~ACTION_PASS_TO_USER; |
| isWakeKey = false; |
| if (!down) { |
| mPowerManagerInternal.setUserInactiveOverrideFromWindowManager(); |
| } |
| break; |
| } |
| |
| case KeyEvent.KEYCODE_WAKEUP: { |
| result &= ~ACTION_PASS_TO_USER; |
| isWakeKey = true; |
| break; |
| } |
| |
| case KeyEvent.KEYCODE_MEDIA_PLAY: |
| case KeyEvent.KEYCODE_MEDIA_PAUSE: |
| case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: |
| case KeyEvent.KEYCODE_HEADSETHOOK: |
| case KeyEvent.KEYCODE_MUTE: |
| 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: { |
| 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_LONG_PRESS); |
| msg.setAsynchronous(true); |
| msg.sendToTarget(); |
| } |
| if (!down && !longPressed) { |
| Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(), |
| 0 /* unused */, null /* hint */); |
| msg.setAsynchronous(true); |
| msg.sendToTarget(); |
| } |
| 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(); |
| } |
| 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; |
| } |
| } |
| |
| if (useHapticFeedback) { |
| performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); |
| } |
| |
| if (isWakeKey) { |
| wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY"); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * 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.getKeyCode()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Notify the StatusBar that a system key was pressed. |
| */ |
| private void sendSystemKeyToStatusBar(int keyCode) { |
| IStatusBarService statusBar = getStatusBarService(); |
| if (statusBar != null) { |
| try { |
| statusBar.handleSystemKey(keyCode); |
| } catch (RemoteException e) { |
| // Oh well. |
| } |
| } |
| } |
| |
| /** |
| * Notify the StatusBar that a system key was pressed without blocking the current thread. |
| */ |
| private void sendSystemKeyToStatusBarAsync(int keyCode) { |
| Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyCode, 0); |
| message.setAsynchronous(true); |
| mHandler.sendMessage(message); |
| } |
| |
| /** |
| * Notify the StatusBar that system rotation suggestion has changed. |
| */ |
| private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) { |
| StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); |
| if (statusBar != null) { |
| statusBar.onProposedRotationChanged(rotation, isValid); |
| } |
| } |
| |
| /** |
| * 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) { |
| // ignore volume keys unless docked |
| case KeyEvent.KEYCODE_VOLUME_UP: |
| case KeyEvent.KEYCODE_VOLUME_DOWN: |
| case KeyEvent.KEYCODE_VOLUME_MUTE: |
| return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED; |
| |
| // ignore media and camera keys |
| case KeyEvent.KEYCODE_MUTE: |
| case KeyEvent.KEYCODE_HEADSETHOOK: |
| case KeyEvent.KEYCODE_MEDIA_PLAY: |
| case KeyEvent.KEYCODE_MEDIA_PAUSE: |
| case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: |
| case KeyEvent.KEYCODE_MEDIA_STOP: |
| case KeyEvent.KEYCODE_MEDIA_NEXT: |
| case KeyEvent.KEYCODE_MEDIA_PREVIOUS: |
| case KeyEvent.KEYCODE_MEDIA_REWIND: |
| case KeyEvent.KEYCODE_MEDIA_RECORD: |
| case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: |
| case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: |
| case KeyEvent.KEYCODE_CAMERA: |
| return false; |
| } |
| return true; |
| } |
| |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { |
| if ((policyFlags & FLAG_WAKE) != 0) { |
| if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion, |
| "android.policy:MOTION")) { |
| return 0; |
| } |
| } |
| |
| if (shouldDispatchInputWhenNonInteractive(null)) { |
| 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) { |
| wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming, |
| "android.policy:MOTION"); |
| } |
| |
| return 0; |
| } |
| |
| private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) { |
| final boolean displayOff = (mDisplay == null || mDisplay.getState() == STATE_OFF); |
| |
| if (displayOff && !mHasFeatureWatch) { |
| return false; |
| } |
| |
| // Send events to keyguard while the screen is on and it's showing. |
| if (isKeyguardShowingAndNotOccluded() && !displayOff) { |
| return true; |
| } |
| |
| // Watches handle BACK specially |
| if (mHasFeatureWatch |
| && event != null |
| && (event.getKeyCode() == KeyEvent.KEYCODE_BACK |
| || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) { |
| return false; |
| } |
| |
| // Send events to a dozing dream even if the screen is off 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; |
| } |
| |
| private void dispatchDirectAudioEvent(KeyEvent event) { |
| if (event.getAction() != KeyEvent.ACTION_DOWN) { |
| return; |
| } |
| int keyCode = event.getKeyCode(); |
| int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND |
| | AudioManager.FLAG_FROM_KEY; |
| String pkgName = mContext.getOpPackageName(); |
| switch (keyCode) { |
| case KeyEvent.KEYCODE_VOLUME_UP: |
| try { |
| getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE, |
| AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG); |
| } catch (Exception e) { |
| Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e); |
| } |
| break; |
| case KeyEvent.KEYCODE_VOLUME_DOWN: |
| try { |
| getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER, |
| AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG); |
| } catch (Exception e) { |
| Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e); |
| } |
| break; |
| case KeyEvent.KEYCODE_VOLUME_MUTE: |
| try { |
| if (event.getRepeatCount() == 0) { |
| getAudioService().adjustSuggestedStreamVolume( |
| AudioManager.ADJUST_TOGGLE_MUTE, |
| AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG); |
| } |
| } catch (Exception e) { |
| Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e); |
| } |
| break; |
| } |
| } |
| |
| 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 { |
| IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface( |
| ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); |
| if (dic != null) { |
| try { |
| dic.exitIdle("voice-search"); |
| } catch (RemoteException e) { |
| } |
| } |
| 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())) { |
| mDockMode = 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); |
| synchronized (mLock) { |
| updateOrientationListenerLp(); |
| } |
| } |
| }; |
| |
| BroadcastReceiver mDreamReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) { |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onDreamingStarted(); |
| } |
| } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) { |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onDreamingStopped(); |
| } |
| } |
| } |
| }; |
| |
| 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); |
| |
| // force a re-application of focused window sysui visibility. |
| // the window may never have been shown for this user |
| // e.g. the keyguard when going through the new-user setup flow |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| mLastSystemUiFlags = 0; |
| updateSystemUiVisibilityLw(); |
| } |
| } |
| } |
| }; |
| |
| private final Runnable mHiddenNavPanic = new Runnable() { |
| @Override |
| public void run() { |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| if (!isUserSetupComplete()) { |
| // Swipe-up for navigation bar is disabled during setup |
| return; |
| } |
| mPendingPanicGestureUptime = SystemClock.uptimeMillis(); |
| if (!isNavBarEmpty(mLastSystemUiFlags)) { |
| mNavigationBarController.showTransient(); |
| } |
| } |
| } |
| }; |
| |
| private void requestTransientBars(WindowState swipeTarget) { |
| synchronized (mWindowManagerFuncs.getWindowManagerLock()) { |
| if (!isUserSetupComplete()) { |
| // Swipe-up for navigation bar is disabled during setup |
| return; |
| } |
| boolean sb = mStatusBarController.checkShowTransientBarLw(); |
| boolean nb = mNavigationBarController.checkShowTransientBarLw() |
| && !isNavBarEmpty(mLastSystemUiFlags); |
| if (sb || nb) { |
| // Don't show status bar when swiping on already visible navigation bar |
| if (!nb && swipeTarget == mNavigationBar) { |
| if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); |
| return; |
| } |
| if (sb) mStatusBarController.showTransient(); |
| if (nb) mNavigationBarController.showTransient(); |
| mImmersiveModeConfirmation.confirmCurrentPrompt(); |
| updateSystemUiVisibilityLw(); |
| } |
| } |
| } |
| |
| // Called on the PowerManager's Notifier thread. |
| @Override |
| public void startedGoingToSleep(int why) { |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")"); |
| |
| mGoingToSleep = true; |
| mRequestedOrGoingToSleep = true; |
| |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onStartedGoingToSleep(why); |
| } |
| } |
| |
| // Called on the PowerManager's Notifier thread. |
| @Override |
| public void finishedGoingToSleep(int why) { |
| EventLog.writeEvent(70000, 0); |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Finished going to sleep... (why=" + why + ")"); |
| MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000); |
| |
| mGoingToSleep = false; |
| mRequestedOrGoingToSleep = 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) { |
| mAwake = false; |
| updateWakeGestureListenerLp(); |
| updateOrientationListenerLp(); |
| updateLockScreenTimeout(); |
| } |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onFinishedGoingToSleep(why, |
| mCameraGestureTriggeredDuringGoingToSleep); |
| } |
| mCameraGestureTriggeredDuringGoingToSleep = false; |
| } |
| |
| // Called on the PowerManager's Notifier thread. |
| @Override |
| public void startedWakingUp() { |
| EventLog.writeEvent(70000, 1); |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up..."); |
| |
| // 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) { |
| mAwake = true; |
| |
| updateWakeGestureListenerLp(); |
| updateOrientationListenerLp(); |
| updateLockScreenTimeout(); |
| } |
| |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onStartedWakingUp(); |
| } |
| } |
| |
| // Called on the PowerManager's Notifier thread. |
| @Override |
| public void finishedWakingUp() { |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Finished waking up..."); |
| |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onFinishedWakingUp(); |
| } |
| } |
| |
| private void wakeUpFromPowerKey(long eventTime) { |
| wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER"); |
| } |
| |
| private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) { |
| final boolean theaterModeEnabled = isTheaterModeEnabled(); |
| if (!wakeInTheaterMode && theaterModeEnabled) { |
| return false; |
| } |
| |
| if (theaterModeEnabled) { |
| Settings.Global.putInt(mContext.getContentResolver(), |
| Settings.Global.THEATER_MODE_ON, 0); |
| } |
| |
| mPowerManager.wakeUp(wakeTime, reason); |
| return true; |
| } |
| |
| private void finishKeyguardDrawn() { |
| synchronized (mLock) { |
| if (!mScreenOnEarly || mKeyguardDrawComplete) { |
| return; // We are not awake yet or we have already informed of this event. |
| } |
| |
| mKeyguardDrawComplete = true; |
| if (mKeyguardDelegate != null) { |
| mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); |
| } |
| mWindowManagerDrawComplete = false; |
| } |
| |
| // ... eventually calls finishWindowsDrawn which will finalize our screen turn on |
| // as well as enabling the orientation change logic/sensor. |
| mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback, |
| WAITING_FOR_DRAWN_TIMEOUT); |
| } |
| |
| // Called on the DisplayManager's DisplayPowerController thread. |
| @Override |
| public void screenTurnedOff() { |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off..."); |
| |
| updateScreenOffSleepToken(true); |
| synchronized (mLock) { |
| mScreenOnEarly = false; |
| mScreenOnFully = false; |
| mKeyguardDrawComplete = false; |
| mWindowManagerDrawComplete = false; |
| mScreenOnListener = null; |
| updateOrientationListenerLp(); |
| |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onScreenTurnedOff(); |
| } |
| } |
| 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 ? 1000 : 5000; |
| } |
| |
| // Called on the DisplayManager's DisplayPowerController thread. |
| @Override |
| public void screenTurningOn(final ScreenOnListener screenOnListener) { |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on..."); |
| |
| updateScreenOffSleepToken(false); |
| synchronized (mLock) { |
| mScreenOnEarly = true; |
| mScreenOnFully = false; |
| mKeyguardDrawComplete = false; |
| mWindowManagerDrawComplete = false; |
| mScreenOnListener = screenOnListener; |
| |
| 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); |
| } |
| } |
| } |
| |
| // Called on the DisplayManager's DisplayPowerController thread. |
| @Override |
| public void screenTurnedOn() { |
| synchronized (mLock) { |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onScreenTurnedOn(); |
| } |
| } |
| reportScreenStateToVrManager(true); |
| } |
| |
| @Override |
| public void screenTurningOff(ScreenOffListener screenOffListener) { |
| mWindowManagerFuncs.screenTurningOff(screenOffListener); |
| synchronized (mLock) { |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.onScreenTurningOff(); |
| } |
| } |
| } |
| |
| private void reportScreenStateToVrManager(boolean isScreenOn) { |
| if (mVrManagerInternal == null) { |
| return; |
| } |
| mVrManagerInternal.onScreenStateChanged(isScreenOn); |
| } |
| |
| private void finishWindowsDrawn() { |
| synchronized (mLock) { |
| if (!mScreenOnEarly || mWindowManagerDrawComplete) { |
| return; // Screen is not turned on or we did already handle this case earlier. |
| } |
| |
| mWindowManagerDrawComplete = true; |
| } |
| |
| finishScreenTurningOn(); |
| } |
| |
| private void finishScreenTurningOn() { |
| synchronized (mLock) { |
| // 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. |
| updateOrientationListenerLp(); |
| } |
| final ScreenOnListener listener; |
| final boolean enableScreen; |
| synchronized (mLock) { |
| if (DEBUG_WAKEUP) Slog.d(TAG, |
| "finishScreenTurningOn: mAwake=" + mAwake |
| + ", mScreenOnEarly=" + mScreenOnEarly |
| + ", mScreenOnFully=" + mScreenOnFully |
| + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete |
| + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); |
| |
| if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete |
| || (mAwake && !mKeyguardDrawComplete)) { |
| return; // spurious or not ready yet |
| } |
| |
| if (DEBUG_WAKEUP) Slog.i(TAG, "Finished screen turning on..."); |
| listener = mScreenOnListener; |
| mScreenOnListener = null; |
| mScreenOnFully = true; |
| |
| // 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 && mAwake) { |
| mKeyguardDrawnOnce = true; |
| enableScreen = true; |
| if (mBootMessageNeedsHiding) { |
| mBootMessageNeedsHiding = false; |
| hideBootMessages(); |
| } |
| } else { |
| enableScreen = false; |
| } |
| } |
| |
| if (listener != null) { |
| listener.onScreenOn(); |
| } |
| |
| if (enableScreen) { |
| try { |
| mWindowManager.enableScreenIfNeeded(); |
| } catch (RemoteException unhandled) { |
| } |
| } |
| } |
| |
| 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() { |
| synchronized (mLock) { |
| return mScreenOnEarly; |
| } |
| } |
| |
| @Override |
| public boolean okToAnimate() { |
| return mAwake && !mGoingToSleep; |
| } |
| |
| /** {@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 isKeyguardShowingAndNotOccluded() { |
| if (mKeyguardDelegate == null) return false; |
| return mKeyguardDelegate.isShowing() && !mKeyguardOccluded; |
| } |
| |
| @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 mKeyguardOccluded; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public boolean inKeyguardRestrictedKeyInputMode() { |
| if (mKeyguardDelegate == null) return false; |
| return mKeyguardDelegate.isInputRestricted(); |
| } |
| |
| @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 boolean isShowingDreamLw() { |
| return mShowingDream; |
| } |
| |
| @Override |
| public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { |
| if (mKeyguardDelegate != null) { |
| if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation"); |
| mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration); |
| } |
| } |
| |
| @Override |
| public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, |
| DisplayCutout displayCutout, Rect outInsets) { |
| outInsets.setEmpty(); |
| |
| // Navigation bar and status bar. |
| getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets); |
| outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]); |
| } |
| |
| @Override |
| public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, |
| DisplayCutout displayCutout, Rect outInsets) { |
| outInsets.setEmpty(); |
| |
| // Only navigation bar |
| if (mHasNavigationBar) { |
| int position = navigationBarPosition(displayWidth, displayHeight, displayRotation); |
| if (position == NAV_BAR_BOTTOM) { |
| outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode); |
| } else if (position == NAV_BAR_RIGHT) { |
| outInsets.right = getNavigationBarWidth(displayRotation, mUiMode); |
| } else if (position == NAV_BAR_LEFT) { |
| outInsets.left = getNavigationBarWidth(displayRotation, mUiMode); |
| } |
| } |
| |
| if (displayCutout != null) { |
| outInsets.left += displayCutout.getSafeInsetLeft(); |
| outInsets.top += displayCutout.getSafeInsetTop(); |
| outInsets.right += displayCutout.getSafeInsetRight(); |
| outInsets.bottom += displayCutout.getSafeInsetBottom(); |
| } |
| } |
| |
| @Override |
| public boolean isNavBarForcedShownLw(WindowState windowState) { |
| return mForceShowSystemBars; |
| } |
| |
| @Override |
| public int getNavBarPosition() { |
| // TODO(multi-display): Support system decor on secondary displays. |
| return mNavigationBarPosition; |
| } |
| |
| @Override |
| public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth, |
| int displayHeight, int displayRotation) { |
| final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation); |
| return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove); |
| } |
| |
| @VisibleForTesting |
| static boolean isDockSideAllowed(int dockSide, int originalDockSide, |
| int navBarPosition, boolean navigationBarCanMove) { |
| if (dockSide == DOCKED_TOP) { |
| return true; |
| } |
| |
| if (navigationBarCanMove) { |
| // Only allow the dockside opposite to the nav bar position in landscape |
| return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT |
| || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT; |
| } |
| |
| // Side is the same as original side |
| if (dockSide == originalDockSide) { |
| return true; |
| } |
| |
| // Only if original docked side was top in portrait will allow left for landscape |
| if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) { |
| return true; |
| } |
| return false; |
| } |
| |
| void sendCloseSystemWindows() { |
| PhoneWindow.sendCloseSystemWindows(mContext, null); |
| } |
| |
| void sendCloseSystemWindows(String reason) { |
| PhoneWindow.sendCloseSystemWindows(mContext, reason); |
| } |
| |
| @Override |
| public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) { |
| if (false) { |
| Slog.v(TAG, "rotationForOrientationLw(orient=" |
| + orientation + ", last=" + lastRotation |
| + "); user=" + mUserRotation + " " |
| + ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) |
| ? "USER_ROTATION_LOCKED" : "") |
| ); |
| } |
| |
| if (mForceDefaultOrientation) { |
| return Surface.ROTATION_0; |
| } |
| |
| synchronized (mLock) { |
| int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1 |
| if (sensorRotation < 0) { |
| sensorRotation = lastRotation; |
| } |
| |
| final int preferredRotation; |
| if (!defaultDisplay) { |
| // For secondary displays we ignore things like displays sensors, docking mode and |
| // rotation lock, and always prefer a default rotation. |
| preferredRotation = Surface.ROTATION_0; |
| } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) { |
| // Ignore sensor when lid switch is open and rotation is forced. |
| preferredRotation = mLidOpenRotation; |
| } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR |
| && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) { |
| // Ignore sensor when in car dock unless explicitly enabled. |
| // This case can override the behavior of NOSENSOR, and can also |
| // enable 180 degree rotation while docked. |
| preferredRotation = mCarDockEnablesAccelerometer |
| ? sensorRotation : mCarDockRotation; |
| } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) |
| && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) { |
| // Ignore sensor when in desk dock unless explicitly enabled. |
| // This case can override the behavior of NOSENSOR, and can also |
| // enable 180 degree rotation while docked. |
| preferredRotation = mDeskDockEnablesAccelerometer |
| ? sensorRotation : mDeskDockRotation; |
| } else if (mHdmiPlugged && mDemoHdmiRotationLock) { |
| // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled. |
| // Note that the dock orientation overrides the HDMI orientation. |
| preferredRotation = mDemoHdmiRotation; |
| } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED |
| && mUndockedHdmiRotation >= 0) { |
| // Ignore sensor when plugged into HDMI and an undocked orientation has |
| // been specified in the configuration (only for legacy devices without |
| // full multi-display support). |
| // Note that the dock orientation overrides the HDMI orientation. |
| preferredRotation = mUndockedHdmiRotation; |
| } else if (mDemoRotationLock) { |
| // Ignore sensor when demo rotation lock is enabled. |
| // Note that the dock orientation and HDMI rotation lock override this. |
| preferredRotation = mDemoRotation; |
| } else if (mPersistentVrModeEnabled) { |
| // While in VR, apps always prefer a portrait rotation. This does not change |
| // any apps that explicitly set landscape, but does cause sensors be ignored, |
| // and ignored any orientation lock that the user has set (this conditional |
| // should remain above the ORIENTATION_LOCKED conditional below). |
| preferredRotation = mPortraitRotation; |
| } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { |
| // Application just wants to remain locked in the last rotation. |
| preferredRotation = lastRotation; |
| } else if (!mSupportAutoRotation) { |
| // If we don't support auto-rotation then bail out here and ignore |
| // the sensor and any rotation lock settings. |
| preferredRotation = -1; |
| } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE |
| && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER)) |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { |
| // Otherwise, use sensor only if requested by the application or enabled |
| // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR. |
| if (mAllowAllRotations < 0) { |
| // Can't read this during init() because the context doesn't |
| // have display metrics at that time so we cannot determine |
| // tablet vs. phone then. |
| mAllowAllRotations = mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0; |
| } |
| if (sensorRotation != Surface.ROTATION_180 |
| || mAllowAllRotations == 1 |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR |
| || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) { |
| preferredRotation = sensorRotation; |
| } else { |
| preferredRotation = lastRotation; |
| } |
| } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED |
| && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { |
| // Apply rotation lock. Does not apply to NOSENSOR. |
| // The idea is that the user rotation expresses a weak preference for the direction |
| // of gravity and as NOSENSOR is never affected by gravity, then neither should |
| // NOSENSOR be affected by rotation lock (although it will be affected by docks). |
| preferredRotation = mUserRotation; |
| } else { |
| // No overriding preference. |
| // We will do exactly what the application asked us to do. |
| preferredRotation = -1; |
| } |
| |
| switch (orientation) { |
| case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: |
| // Return portrait unless overridden. |
| if (isAnyPortrait(preferredRotation)) { |
| return preferredRotation; |
| } |
| return mPortraitRotation; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: |
| // Return landscape unless overridden. |
| if (isLandscapeOrSeascape(preferredRotation)) { |
| return preferredRotation; |
| } |
| return mLandscapeRotation; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: |
| // Return reverse portrait unless overridden. |
| if (isAnyPortrait(preferredRotation)) { |
| return preferredRotation; |
| } |
| return mUpsideDownRotation; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: |
| // Return seascape unless overridden. |
| if (isLandscapeOrSeascape(preferredRotation)) { |
| return preferredRotation; |
| } |
| return mSeascapeRotation; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: |
| case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: |
| // Return either landscape rotation. |
| if (isLandscapeOrSeascape(preferredRotation)) { |
| return preferredRotation; |
| } |
| if (isLandscapeOrSeascape(lastRotation)) { |
| return lastRotation; |
| } |
| return mLandscapeRotation; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: |
| case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: |
| // Return either portrait rotation. |
| if (isAnyPortrait(preferredRotation)) { |
| return preferredRotation; |
| } |
| if (isAnyPortrait(lastRotation)) { |
| return lastRotation; |
| } |
| return mPortraitRotation; |
| |
| default: |
| // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR, |
| // just return the preferred orientation we already calculated. |
| if (preferredRotation >= 0) { |
| return preferredRotation; |
| } |
| return Surface.ROTATION_0; |
| } |
| } |
| } |
| |
| @Override |
| public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation) { |
| switch (orientation) { |
| case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: |
| case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: |
| case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: |
| return isAnyPortrait(rotation); |
| |
| case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: |
| case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: |
| case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: |
| return isLandscapeOrSeascape(rotation); |
| |
| default: |
| return true; |
| } |
| } |
| |
| @Override |
| public void setRotationLw(int rotation) { |
| mOrientationListener.setCurrentRotation(rotation); |
| } |
| |
| public boolean isRotationChoicePossible(int orientation) { |
| // Rotation choice is only shown when the user is in locked mode. |
| if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false; |
| |
| // We should only enable rotation choice if the rotation isn't forced by the lid, dock, |
| // demo, hdmi, vr, etc mode |
| |
| // Determine if the rotation is currently forced |
| if (mForceDefaultOrientation) { |
| return false; // Rotation is forced to default orientation |
| |
| } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) { |
| return false; // Rotation is forced mLidOpenRotation |
| |
| } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && !mCarDockEnablesAccelerometer) { |
| return false; // Rotation forced to mCarDockRotation |
| |
| } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) |
| && !mDeskDockEnablesAccelerometer) { |
| return false; // Rotation forced to mDeskDockRotation |
| |
| } else if (mHdmiPlugged && mDemoHdmiRotationLock) { |
| return false; // Rotation forced to mDemoHdmiRotation |
| |
| } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED |
| && mUndockedHdmiRotation >= 0) { |
| return false; // Rotation forced to mUndockedHdmiRotation |
| |
| } else if (mDemoRotationLock) { |
| return false; // Rotation forced to mDemoRotation |
| |
| } else if (mPersistentVrModeEnabled) { |
| return false; // Rotation forced to mPortraitRotation |
| |
| } else if (!mSupportAutoRotation) { |
| return false; |
| } |
| |
| // Ensure that some rotation choice is possible for the given orientation |
| switch (orientation) { |
| case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: |
| case ActivityInfo.SCREEN_ORIENTATION_USER: |
| case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: |
| case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: |
| case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: |
| // NOSENSOR description is ambiguous, in reality WM ignores user choice |
| return true; |
| } |
| |
| // Rotation is forced, should be controlled by system |
| return false; |
| } |
| |
| public boolean isValidRotationChoice(int orientation, final int preferredRotation) { |
| // Determine if the given app orientation is compatible with the provided rotation choice |
| switch (orientation) { |
| case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: |
| // Works with any of the 4 rotations |
| return preferredRotation >= 0; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: |
| // It's possible for the user pref to be set at 180 because of FULL_USER. This would |
| // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait |
| // but never to go to 180. |
| return preferredRotation == mPortraitRotation; |
| |
| case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: |
| // Works landscape or seascape |
| return isLandscapeOrSeascape(preferredRotation); |
| |
| case ActivityInfo.SCREEN_ORIENTATION_USER: |
| case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: |
| // Works with any rotation except upside down |
| return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation); |
| } |
| |
| return false; |
| } |
| |
| private boolean isLandscapeOrSeascape(int rotation) { |
| return rotation == mLandscapeRotation || rotation == mSeascapeRotation; |
| } |
| |
| private boolean isAnyPortrait(int rotation) { |
| return rotation == mPortraitRotation || rotation == mUpsideDownRotation; |
| } |
| |
| @Override |
| public int getUserRotationMode() { |
| return Settings.System.getIntForUser(mContext.getContentResolver(), |
| Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ? |
| WindowManagerPolicy.USER_ROTATION_FREE : |
| WindowManagerPolicy.USER_ROTATION_LOCKED; |
| } |
| |
| // User rotation: to be used when all else fails in assigning an orientation to the device |
| @Override |
| public void setUserRotationMode(int mode, int rot) { |
| ContentResolver res = mContext.getContentResolver(); |
| |
| // mUserRotationMode and mUserRotation will be assigned by the content observer |
| if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) { |
| Settings.System.putIntForUser(res, |
| Settings.System.USER_ROTATION, |
| rot, |
| UserHandle.USER_CURRENT); |
| Settings.System.putIntForUser(res, |
| Settings.System.ACCELEROMETER_ROTATION, |
| 0, |
| UserHandle.USER_CURRENT); |
| } else { |
| Settings.System.putIntForUser(res, |
| Settings.System.ACCELEROMETER_ROTATION, |
| 1, |
| UserHandle.USER_CURRENT); |
| } |
| } |
| |
| @Override |
| public void setSafeMode(boolean safeMode) { |
| mSafeMode = safeMode; |
| if (safeMode) { |
| performHapticFeedbackLw(null, HapticFeedbackConstants.SAFE_MODE_ENABLED, true); |
| } |
| } |
| |
| static long[] getLongIntArray(Resources r, int resid) { |
| return ArrayUtils.convertToLongArray(r.getIntArray(resid)); |
| } |
| |
| 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(); |
| synchronized (mLock) { |
| updateOrientationListenerLp(); |
| mSystemReady = true; |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| updateSettings(); |
| } |
| }); |
| // If this happens, for whatever reason, systemReady came later than systemBooted. |
| // And keyguard should be already bound from systemBooted |
| if (mSystemBooted) { |
| mKeyguardDelegate.onBootCompleted(); |
| } |
| } |
| |
| mSystemGestures.systemReady(); |
| mImmersiveModeConfirmation.systemReady(); |
| |
| mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void systemBooted() { |
| bindKeyguard(); |
| synchronized (mLock) { |
| mSystemBooted = true; |
| if (mSystemReady) { |
| mKeyguardDelegate.onBootCompleted(); |
| } |
| } |
| startedWakingUp(); |
| screenTurningOn(null); |
| screenTurnedOn(); |
| } |
| |
| @Override |
| public boolean canDismissBootAnimation() { |
| synchronized (mLock) { |
| return mKeyguardDrawComplete; |
| } |
| } |
| |
| 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 (mContext.getPackageManager().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 (mContext.getPackageManager().isUpgrade()) { |
| 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; |
| mBootMsgDialog.getWindow().setAttributes(lp); |
| mBootMsgDialog.setCancelable(false); |
| mBootMsgDialog.show(); |
| } |
| mBootMsgDialog.setMessage(msg); |
| } |
| }); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void hideBootMessages() { |
| mHandler.sendEmptyMessage(MSG_HIDE_BOOT_MESSAGE); |
| } |
| |
| @Override |
| public void requestUserActivityNotification() { |
| if (!mNotifyUserActivity && !mHandler.hasMessages(MSG_NOTIFY_USER_ACTIVITY)) { |
| mNotifyUserActivity = true; |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void userActivity() { |
| // *************************************** |
| // NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE |
| // *************************************** |
| // THIS IS CALLED FROM DEEP IN THE POWER MANAGER |
| // WITH ITS LOCKS HELD. |
| // |
| // This code must be VERY careful about the locks |
| // it acquires. |
| // In fact, the current code acquires way too many, |
| // and probably has lurking deadlocks. |
| |
| synchronized (mScreenLockTimeout) { |
| if (mLockScreenTimerActive) { |
| // reset the timer |
| mHandler.removeCallbacks(mScreenLockTimeout); |
| mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout); |
| } |
| } |
| |
| if (mAwake && mNotifyUserActivity) { |
| mHandler.sendEmptyMessageDelayed(MSG_NOTIFY_USER_ACTIVITY, |
| USER_ACTIVITY_NOTIFICATION_DELAY); |
| mNotifyUserActivity = false; |
| } |
| } |
| |
| 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; |
| options = null; |
| } |
| } |
| |
| public void setLockOptions(Bundle options) { |
| this.options = options; |
| } |
| } |
| |
| 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); |
| } |
| |
| private void updateLockScreenTimeout() { |
| synchronized (mScreenLockTimeout) { |
| boolean enable = (mAllowLockscreenWhenOn && mAwake && |
| 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 updateDreamingSleepToken(boolean acquire) { |
| if (acquire) { |
| if (mDreamingSleepToken == null) { |
| mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken( |
| "Dream", DEFAULT_DISPLAY); |
| } |
| } else { |
| if (mDreamingSleepToken != null) { |
| mDreamingSleepToken.release(); |
| mDreamingSleepToken = null; |
| } |
| } |
| } |
| |
| // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. |
| private void updateScreenOffSleepToken(boolean acquire) { |
| if (acquire) { |
| if (mScreenOffSleepToken == null) { |
| mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken( |
| "ScreenOff", DEFAULT_DISPLAY); |
| } |
| } else { |
| if (mScreenOffSleepToken != null) { |
| mScreenOffSleepToken.release(); |
| mScreenOffSleepToken = null; |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void enableScreenAfterBoot() { |
| readLidState(); |
| applyLidSwitchState(); |
| updateRotation(true); |
| } |
| |
| private void applyLidSwitchState() { |
| if (mLidState == LID_CLOSED && mLidControlsSleep) { |
| goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, |
| PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); |
| } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) { |
| mWindowManagerFuncs.lockDeviceNow(); |
| } |
| |
| 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) { |
| } |
| } |
| |
| void updateRotation(boolean alwaysSendConfiguration) { |
| try { |
| //set orientation on WindowManager |
| mWindowManager.updateRotation(alwaysSendConfiguration, false); |
| } catch (RemoteException e) { |
| // Ignore |
| } |
| } |
| |
| void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { |
| try { |
| //set orientation on WindowManager |
| mWindowManager.updateRotation(alwaysSendConfiguration, forceRelayout); |
| } catch (RemoteException e) { |
| // Ignore |
| } |
| } |
| |
| /** |
| * 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 |
| && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK |
| || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK |
| || mDockMode == 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 = mContext.getPackageManager().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(boolean fromHomeKey, boolean awakenFromDreams) { |
| try { |
| ActivityManager.getService().stopAppSwitches(); |
| } catch (RemoteException e) {} |
| sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); |
| |
| if (awakenFromDreams) { |
| awakenDreams(); |
| } |
| |
| 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) { |
| } |
| } |
| |
| Intent intent; |
| |
| if (fromHomeKey) { |
| intent = new Intent(mHomeIntent); |
| intent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey); |
| } else { |
| intent = mHomeIntent; |
| } |
| |
| startActivityAsUser(intent, UserHandle.CURRENT); |
| } |
| |
| /** |
| * 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(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(); |
| Intent dock = createHomeDockIntent(); |
| if (dock != null) { |
| int result = ActivityManager.getService() |
| .startActivityAsUser(null, null, 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 = ActivityManager.getService() |
| .startActivityAsUser(null, null, 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; |
| } |
| |
| @Override |
| public void setCurrentOrientationLw(int newOrientation) { |
| synchronized (mLock) { |
| if (newOrientation != mCurrentAppOrientation) { |
| mCurrentAppOrientation = newOrientation; |
| updateOrientationListenerLp(); |
| } |
| } |
| } |
| |
| private boolean isTheaterModeEnabled() { |
| return Settings.Global.getInt(mContext.getContentResolver(), |
| Settings.Global.THEATER_MODE_ON, 0) == 1; |
| } |
| |
| @Override |
| public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) { |
| if (!mVibrator.hasVibrator()) { |
| return false; |
| } |
| final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(), |
| Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0; |
| if (hapticsDisabled && !always) { |
| return false; |
| } |
| |
| VibrationEffect effect = getVibrationEffect(effectId); |
| if (effect == null) { |
| return false; |
| } |
| |
| int owningUid; |
| String owningPackage; |
| if (win != null) { |
| owningUid = win.getOwningUid(); |
| owningPackage = win.getOwningPackage(); |
| } else { |
| owningUid = android.os.Process.myUid(); |
| owningPackage = mContext.getOpPackageName(); |
| } |
| mVibrator.vibrate(owningUid, owningPackage, effect, VIBRATION_ATTRIBUTES); |
| return true; |
| } |
| |
| private VibrationEffect getVibrationEffect(int effectId) { |
| long[] pattern; |
| switch (effectId) { |
| case HapticFeedbackConstants.CLOCK_TICK: |
| case HapticFeedbackConstants.CONTEXT_CLICK: |
| return VibrationEffect.get(VibrationEffect.EFFECT_TICK); |
| case HapticFeedbackConstants.KEYBOARD_RELEASE: |
| case HapticFeedbackConstants.TEXT_HANDLE_MOVE: |
| case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: |
| case HapticFeedbackConstants.ENTRY_BUMP: |
| case HapticFeedbackConstants.DRAG_CROSSING: |
| case HapticFeedbackConstants.GESTURE_END: |
| return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); |
| case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS |
| case HapticFeedbackConstants.VIRTUAL_KEY: |
| case HapticFeedbackConstants.EDGE_RELEASE: |
| case HapticFeedbackConstants.CONFIRM: |
| case HapticFeedbackConstants.GESTURE_START: |
| return VibrationEffect.get(VibrationEffect.EFFECT_CLICK); |
| case HapticFeedbackConstants.LONG_PRESS: |
| case HapticFeedbackConstants.EDGE_SQUEEZE: |
| return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); |
| case HapticFeedbackConstants.REJECT: |
| return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); |
| |
| case HapticFeedbackConstants.CALENDAR_DATE: |
| pattern = mCalendarDateVibePattern; |
| break; |
| case HapticFeedbackConstants.SAFE_MODE_ENABLED: |
| pattern = mSafeModeEnabledVibePattern; |
| break; |
| |
| default: |
| return null; |
| } |
| if (pattern.length == 0) { |
| // No vibration |
| return null; |
| } else if (pattern.length == 1) { |
| // One-shot vibration |
| return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE); |
| } else { |
| // Pattern vibration |
| return VibrationEffect.createWaveform(pattern, -1); |
| } |
| } |
| |
| @Override |
| public void keepScreenOnStartedLw() { |
| } |
| |
| @Override |
| public void keepScreenOnStoppedLw() { |
| if (isKeyguardShowingAndNotOccluded()) { |
| mPowerManager.userActivity(SystemClock.uptimeMillis(), false); |
| } |
| } |
| |
| private int updateSystemUiVisibilityLw() { |
| // If there is no window focused, there will be nobody to handle the events |
| // anyway, so just hang on in whatever state we're in until things settle down. |
| WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow |
| : mTopFullscreenOpaqueWindowState; |
| if (winCandidate == null) { |
| return 0; |
| } |
| if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { |
| // The immersive mode confirmation should never affect the system bar visibility, |
| // otherwise it will unhide the navigation bar and hide itself. |
| winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState; |
| if (winCandidate == null) { |
| return 0; |
| } |
| } |
| final WindowState win = winCandidate; |
| if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) { |
| // We are updating at a point where the keyguard has gotten |
| // focus, but we were last in a state where the top window is |
| // hiding it. This is probably because the keyguard as been |
| // shown while the top window was displayed, so we want to ignore |
| // it here because this is just a very transient change and it |
| // will quickly lose focus once it correctly gets hidden. |
| return 0; |
| } |
| |
| int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) |
| & ~mResettingSystemUiFlags |
| & ~mForceClearedSystemUiFlags; |
| if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { |
| tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); |
| } |
| |
| final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */, |
| mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState); |
| final int dockedVisibility = updateLightStatusBarLw(0 /* vis */, |
| mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState); |
| mWindowManagerFuncs.getStackBounds( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); |
| mWindowManagerFuncs.getStackBounds( |
| WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); |
| final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); |
| final int diff = visibility ^ mLastSystemUiFlags; |
| final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; |
| final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags; |
| final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState); |
| if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu |
| && mFocusedApp == win.getAppToken() |
| && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) |
| && mLastDockedStackBounds.equals(mDockedStackBounds)) { |
| return 0; |
| } |
| mLastSystemUiFlags = visibility; |
| mLastFullscreenStackSysUiFlags = fullscreenVisibility; |
| mLastDockedStackSysUiFlags = dockedVisibility; |
| mLastFocusNeedsMenu = needsMenu; |
| mFocusedApp = win.getAppToken(); |
| final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); |
| final Rect dockedStackBounds = new Rect(mDockedStackBounds); |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); |
| if (statusbar != null) { |
| statusbar.setSystemUiVisibility(visibility, fullscreenVisibility, |
| dockedVisibility, 0xffffffff, fullscreenStackBounds, |
| dockedStackBounds, win.toString()); |
| statusbar.topAppWindowChanged(needsMenu); |
| } |
| } |
| }); |
| return diff; |
| } |
| |
| private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) { |
| final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded; |
| final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming; |
| if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) { |
| // If the top fullscreen-or-dimming window is also the top fullscreen, respect |
| // its light flag. |
| vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; |
| vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null) |
| & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; |
| } else if (statusColorWin != null && statusColorWin.isDimming()) { |
| // Otherwise if it's dimming, clear the light flag. |
| vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; |
| } |
| return vis; |
| } |
| |
| @VisibleForTesting |
| @Nullable |
| static WindowState chooseNavigationColorWindowLw(WindowState opaque, |
| WindowState opaqueOrDimming, WindowState imeWindow, |
| @NavigationBarPosition int navBarPosition) { |
| // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME |
| // window can be navigation color window. |
| final boolean imeWindowCanNavColorWindow = imeWindow != null |
| && imeWindow.isVisibleLw() |
| && navBarPosition == NAV_BAR_BOTTOM |
| && (PolicyControl.getWindowFlags(imeWindow, null) |
| & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; |
| |
| if (opaque != null && opaqueOrDimming == opaque) { |
| // If the top fullscreen-or-dimming window is also the top fullscreen, respect it |
| // unless IME window is also eligible, since currently the IME window is always show |
| // above the opaque fullscreen app window, regardless of the IME target window. |
| // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed. |
| return imeWindowCanNavColorWindow ? imeWindow : opaque; |
| } |
| |
| if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) { |
| // No dimming window is involved. Determine the result only with the IME window. |
| return imeWindowCanNavColorWindow ? imeWindow : null; |
| } |
| |
| if (!imeWindowCanNavColorWindow) { |
| // No IME window is involved. Determine the result only with opaqueOrDimming. |
| return opaqueOrDimming; |
| } |
| |
| // The IME window and the dimming window are competing. Check if the dimming window can be |
| // IME target or not. |
| if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) { |
| // The IME window is above the dimming window. |
| return imeWindow; |
| } else { |
| // The dimming window is above the IME window. |
| return opaqueOrDimming; |
| } |
| } |
| |
| @VisibleForTesting |
| static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming, |
| WindowState imeWindow, WindowState navColorWin) { |
| |
| if (navColorWin != null) { |
| if (navColorWin == imeWindow || navColorWin == opaque) { |
| // Respect the light flag. |
| vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; |
| vis |= PolicyControl.getSystemUiVisibility(navColorWin, null) |
| & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; |
| } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { |
| // Clear the light flag for dimming window. |
| vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; |
| } |
| } |
| return vis; |
| } |
| |
| private int updateSystemBarsLw(WindowState win, int oldVis, int vis) { |
| final boolean dockedStackVisible = |
| mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); |
| final boolean freeformStackVisible = |
| mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM); |
| final boolean resizing = mWindowManagerInternal.isDockedDividerResizing(); |
| |
| // We need to force system bars when the docked stack is visible, when the freeform stack |
| // is visible but also when we are resizing for the transitions when docked stack |
| // visibility changes. |
| mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing; |
| final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard; |
| |
| // apply translucent bar vis flags |
| WindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded |
| ? mStatusBar |
| : mTopFullscreenOpaqueWindowState; |
| vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); |
| vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); |
| final int dockedVis = mStatusBarController.applyTranslucentFlagLw( |
| mTopDockedOpaqueWindowState, 0, 0); |
| |
| final boolean fullscreenDrawsStatusBarBackground = |
| drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState); |
| final boolean dockedDrawsStatusBarBackground = |
| drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState); |
| |
| // prevent status bar interaction from clearing certain flags |
| int type = win.getAttrs().type; |
| boolean statusBarHasFocus = type == TYPE_STATUS_BAR; |
| if (statusBarHasFocus && !isStatusBarKeyguard()) { |
| int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_IMMERSIVE |
| | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
| | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; |
| if (mKeyguardOccluded) { |
| flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT; |
| } |
| vis = (vis & ~flags) | (oldVis & flags); |
| } |
| |
| if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) { |
| vis |= View.STATUS_BAR_TRANSPARENT; |
| vis &= ~View.STATUS_BAR_TRANSLUCENT; |
| } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar) |
| || forceOpaqueStatusBar) { |
| vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT); |
| } |
| |
| vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing); |
| |
| // update status bar |
| boolean immersiveSticky = |
| (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; |
| final boolean hideStatusBarWM = |
| mTopFullscreenOpaqueWindowState != null |
| && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) |
| & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; |
| final boolean hideStatusBarSysui = |
| (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; |
| final boolean hideNavBarSysui = |
| (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; |
| |
| final boolean transientStatusBarAllowed = mStatusBar != null |
| && (statusBarHasFocus || (!mForceShowSystemBars |
| && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); |
| |
| final boolean transientNavBarAllowed = mNavigationBar != null |
| && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky; |
| |
| final long now = SystemClock.uptimeMillis(); |
| final boolean pendingPanic = mPendingPanicGestureUptime != 0 |
| && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION; |
| if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) { |
| // The user performed the panic gesture recently, we're about to hide the bars, |
| // we're no longer on the Keyguard and the screen is ready. We can now request the bars. |
| mPendingPanicGestureUptime = 0; |
| mStatusBarController.showTransient(); |
| if (!isNavBarEmpty(vis)) { |
| mNavigationBarController.showTransient(); |
| } |
| } |
| |
| final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested() |
| && !transientStatusBarAllowed && hideStatusBarSysui; |
| final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested() |
| && !transientNavBarAllowed; |
| if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) { |
| // clear the clearable flags instead |
| clearClearableFlagsLw(); |
| vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; |
| } |
| |
| final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; |
| immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; |
| final boolean navAllowedHidden = immersive || immersiveSticky; |
| |
| if (hideNavBarSysui && !navAllowedHidden |
| && getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) { |
| // We can't hide the navbar from this window otherwise the input consumer would not get |
| // the input events. |
| vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); |
| } |
| |
| vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); |
| |
| // update navigation bar |
| boolean oldImmersiveMode = isImmersiveMode(oldVis); |
| boolean newImmersiveMode = isImmersiveMode(vis); |
| if (win != null && oldImmersiveMode != newImmersiveMode) { |
| final String pkg = win.getOwningPackage(); |
| mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, |
| isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility())); |
| } |
| |
| vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); |
| |
| final WindowState navColorWin = chooseNavigationColorWindowLw( |
| mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, |
| mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition); |
| vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, |
| mTopFullscreenOpaqueOrDimmingWindowState, |
| mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin); |
| |
| return vis; |
| } |
| |
| private boolean drawsStatusBarBackground(int vis, WindowState win) { |
| if (!mStatusBarController.isTransparentAllowed(win)) { |
| return false; |
| } |
| if (win == null) { |
| return true; |
| } |
| |
| final boolean drawsSystemBars = |
| (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; |
| final boolean forceDrawsSystemBars = |
| (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; |
| |
| return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0; |
| } |
| |
| /** |
| * @return the current visibility flags with the nav-bar opacity related flags toggled based |
| * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}. |
| */ |
| private int configureNavBarOpacity(int visibility, boolean dockedStackVisible, |
| boolean freeformStackVisible, boolean isDockedDividerResizing) { |
| if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { |
| if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } |
| } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) { |
| if (isDockedDividerResizing) { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } else if (freeformStackVisible) { |
| visibility = setNavBarTranslucentFlag(visibility); |
| } else { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } |
| } |
| |
| if (!areTranslucentBarsAllowed()) { |
| visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT; |
| } |
| return visibility; |
| } |
| |
| private int setNavBarOpaqueFlag(int visibility) { |
| return visibility &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT); |
| } |
| |
| private int setNavBarTranslucentFlag(int visibility) { |
| visibility &= ~View.NAVIGATION_BAR_TRANSPARENT; |
| return visibility |= View.NAVIGATION_BAR_TRANSLUCENT; |
| } |
| |
| private void clearClearableFlagsLw() { |
| int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; |
| if (newVal != mResettingSystemUiFlags) { |
| mResettingSystemUiFlags = newVal; |
| mWindowManagerFuncs.reevaluateStatusBarVisibility(); |
| } |
| } |
| |
| private boolean isImmersiveMode(int vis) { |
| final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; |
| return mNavigationBar != null |
| && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 |
| && (vis & flags) != 0 |
| && canHideNavigationBar(); |
| } |
| |
| private static boolean isNavBarEmpty(int systemUiFlags) { |
| final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME |
| | View.STATUS_BAR_DISABLE_BACK |
| | View.STATUS_BAR_DISABLE_RECENT); |
| |
| return (systemUiFlags & disableNavigationBar) == disableNavigationBar; |
| } |
| |
| /** |
| * @return whether the navigation or status bar can be made translucent |
| * |
| * This should return true unless touch exploration is not enabled or |
| * R.boolean.config_enableTranslucentDecor is false. |
| */ |
| private boolean areTranslucentBarsAllowed() { |
| return mTranslucentDecorEnabled; |
| } |
| |
| // 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 mHasNavigationBar; |
| } |
| |
| @Override |
| public void setLastInputMethodWindowLw(WindowState ime, WindowState target) { |
| mLastInputMethodWindow = ime; |
| mLastInputMethodTargetWindow = target; |
| } |
| |
| @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); |
| } |
| setLastInputMethodWindowLw(null, null); |
| } |
| |
| @Override |
| public void setSwitchingUser(boolean switching) { |
| mKeyguardDelegate.setSwitchingUser(switching); |
| } |
| |
| @Override |
| public boolean isTopLevelWindow(int windowType) { |
| if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW |
| && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { |
| return (windowType == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) { |
| // For the upside down rotation we don't rotate seamlessly as the navigation |
| // bar moves position. |
| // Note most apps (using orientation:sensor or user as opposed to fullSensor) |
| // will not enter the reverse portrait orientation, so actually the |
| // orientation won't change at all. |
| if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) { |
| return false; |
| } |
| // If the navigation bar can't change sides, then it will |
| // jump when we change orientations and we don't rotate |
| // seamlessly. |
| if (!mNavigationBarCanMove) { |
| return false; |
| } |
| int delta = newRotation - oldRotation; |
| if (delta < 0) delta += 4; |
| // Likewise we don't rotate seamlessly for 180 degree rotations |
| // in this case the surfaces never resize, and our logic to |
| // revert the transformations on size change will fail. We could |
| // fix this in the future with the "tagged" frames idea. |
| if (delta == Surface.ROTATION_180) { |
| return false; |
| } |
| |
| final WindowState w = mTopFullscreenOpaqueWindowState; |
| if (w != mFocusedWindow) { |
| return false; |
| } |
| |
| // We only enable seamless rotation if the top window has requested |
| // it and is in the fullscreen opaque state. Seamless rotation |
| // requires freezing various Surface states and won't work well |
| // with animations, so we disable it in the animation case for now. |
| if (w != null && !w.isAnimatingLw() && |
| ((w.getAttrs().rotationAnimation == ROTATION_ANIMATION_JUMPCUT) || |
| (w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS))) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void writeToProto(ProtoOutputStream proto, long fieldId) { |
| final long token = proto.start(fieldId); |
| proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags); |
| proto.write(ROTATION_MODE, mUserRotationMode); |
| proto.write(ROTATION, mUserRotation); |
| proto.write(ORIENTATION, mCurrentAppOrientation); |
| proto.write(SCREEN_ON_FULLY, mScreenOnFully); |
| proto.write(KEYGUARD_DRAW_COMPLETE, mKeyguardDrawComplete); |
| proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mWindowManagerDrawComplete); |
| if (mFocusedApp != null) { |
| proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString()); |
| } |
| if (mFocusedWindow != null) { |
| mFocusedWindow.writeIdentifierToProto(proto, FOCUSED_WINDOW); |
| } |
| if (mTopFullscreenOpaqueWindowState != null) { |
| mTopFullscreenOpaqueWindowState.writeIdentifierToProto( |
| proto, TOP_FULLSCREEN_OPAQUE_WINDOW); |
| } |
| if (mTopFullscreenOpaqueOrDimmingWindowState != null) { |
| mTopFullscreenOpaqueOrDimmingWindowState.writeIdentifierToProto( |
| proto, TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW); |
| } |
| proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded); |
| proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged); |
| proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded); |
| proto.write(FORCE_STATUS_BAR, mForceStatusBar); |
| proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard); |
| mStatusBarController.writeToProto(proto, STATUS_BAR); |
| mNavigationBarController.writeToProto(proto, NAVIGATION_BAR); |
| if (mOrientationListener != null) { |
| mOrientationListener.writeToProto(proto, ORIENTATION_LISTENER); |
| } |
| if (mKeyguardDelegate != null) { |
| mKeyguardDelegate.writeToProto(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("mLidState="); |
| pw.print(WindowManagerFuncs.lidStateToString(mLidState)); |
| pw.print(" mLidOpenRotation="); |
| pw.println(Surface.rotationToString(mLidOpenRotation)); |
| pw.print(prefix); pw.print("mCameraLensCoverState="); |
| pw.print(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState)); |
| pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged); |
| if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 |
| || mForceClearedSystemUiFlags != 0) { |
| pw.print(prefix); pw.print("mLastSystemUiFlags=0x"); |
| pw.print(Integer.toHexString(mLastSystemUiFlags)); |
| pw.print(" mResettingSystemUiFlags=0x"); |
| pw.print(Integer.toHexString(mResettingSystemUiFlags)); |
| pw.print(" mForceClearedSystemUiFlags=0x"); |
| pw.println(Integer.toHexString(mForceClearedSystemUiFlags)); |
| } |
| if (mLastFocusNeedsMenu) { |
| pw.print(prefix); pw.print("mLastFocusNeedsMenu="); |
| pw.println(mLastFocusNeedsMenu); |
| } |
| pw.print(prefix); pw.print("mWakeGestureEnabledSetting="); |
| pw.println(mWakeGestureEnabledSetting); |
| |
| pw.print(prefix); |
| pw.print("mSupportAutoRotation="); pw.print(mSupportAutoRotation); |
| pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled); |
| pw.print(prefix); pw.print("mUiMode="); pw.print(Configuration.uiModeToString(mUiMode)); |
| pw.print(" mDockMode="); pw.println(Intent.dockStateToString(mDockMode)); |
| pw.print(prefix); pw.print("mEnableCarDockHomeCapture="); |
| pw.print(mEnableCarDockHomeCapture); |
| pw.print(" mCarDockRotation="); |
| pw.print(Surface.rotationToString(mCarDockRotation)); |
| pw.print(" mDeskDockRotation="); |
| pw.println(Surface.rotationToString(mDeskDockRotation)); |
| pw.print(prefix); pw.print("mUserRotationMode="); |
| pw.print(WindowManagerPolicy.userRotationModeToString(mUserRotationMode)); |
| pw.print(" mUserRotation="); pw.print(Surface.rotationToString(mUserRotation)); |
| pw.print(" mAllowAllRotations="); |
| pw.println(allowAllRotationsToString(mAllowAllRotations)); |
| pw.print(prefix); pw.print("mCurrentAppOrientation="); |
| pw.println(ActivityInfo.screenOrientationToString(mCurrentAppOrientation)); |
| pw.print(prefix); pw.print("mCarDockEnablesAccelerometer="); |
| pw.print(mCarDockEnablesAccelerometer); |
| pw.print(" mDeskDockEnablesAccelerometer="); |
| pw.println(mDeskDockEnablesAccelerometer); |
| pw.print(prefix); pw.print("mLidKeyboardAccessibility="); |
| pw.print(mLidKeyboardAccessibility); |
| pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility); |
| pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock); |
| pw.print(prefix); pw.print("mLidControlsSleep="); pw.println(mLidControlsSleep); |
| 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("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("mShortPressOnSleepBehavior="); |
| pw.println(shortPressOnSleepBehaviorToString(mShortPressOnSleepBehavior)); |
| pw.print(prefix); |
| pw.print("mShortPressOnWindowBehavior="); |
| pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior)); |
| pw.print(prefix); |
| pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup="); |
| pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup); |
| pw.print(prefix); |
| pw.print("mHasSoftInput="); pw.print(mHasSoftInput); |
| pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed); |
| pw.print(prefix); |
| pw.print("mIncallPowerBehavior="); |
| pw.print(incallPowerBehaviorToString(mIncallPowerBehavior)); |
| pw.print(" mIncallBackBehavior="); |
| pw.print(incallBackBehaviorToString(mIncallBackBehavior)); |
| pw.print(" mEndcallBehavior="); |
| pw.println(endcallBehaviorToString(mEndcallBehavior)); |
| pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed); |
| pw.print(prefix); |
| pw.print("mAwake="); pw.print(mAwake); |
| pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly); |
| pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); |
| pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); |
| pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); |
| pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer); |
| pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer); |
| pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); |
| pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen); |
| pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken); |
| if (mLastInputMethodWindow != null) { |
| pw.print(prefix); pw.print("mLastInputMethodWindow="); |
| pw.println(mLastInputMethodWindow); |
| } |
| if (mLastInputMethodTargetWindow != null) { |
| pw.print(prefix); pw.print("mLastInputMethodTargetWindow="); |
| pw.println(mLastInputMethodTargetWindow); |
| } |
| if (mStatusBar != null) { |
| pw.print(prefix); pw.print("mStatusBar="); |
| pw.print(mStatusBar); pw.print(" isStatusBarKeyguard="); |
| pw.println(isStatusBarKeyguard()); |
| } |
| if (mNavigationBar != null) { |
| pw.print(prefix); pw.print("mNavigationBar="); |
| pw.println(mNavigationBar); |
| } |
| if (mFocusedWindow != null) { |
| pw.print(prefix); pw.print("mFocusedWindow="); |
| pw.println(mFocusedWindow); |
| } |
| if (mFocusedApp != null) { |
| pw.print(prefix); pw.print("mFocusedApp="); |
| pw.println(mFocusedApp); |
| } |
| if (mTopFullscreenOpaqueWindowState != null) { |
| pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState="); |
| pw.println(mTopFullscreenOpaqueWindowState); |
| } |
| if (mTopFullscreenOpaqueOrDimmingWindowState != null) { |
| pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState="); |
| pw.println(mTopFullscreenOpaqueOrDimmingWindowState); |
| } |
| if (mForcingShowNavBar) { |
| pw.print(prefix); pw.print("mForcingShowNavBar="); |
| pw.println(mForcingShowNavBar); pw.print( "mForcingShowNavBarLayer="); |
| pw.println(mForcingShowNavBarLayer); |
| } |
| pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen); |
| pw.print(" mKeyguardOccluded="); pw.println(mKeyguardOccluded); |
| pw.print(prefix); |
| pw.print("mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged); |
| pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded); |
| pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); |
| pw.print(" mForceStatusBarFromKeyguard="); |
| pw.println(mForceStatusBarFromKeyguard); |
| pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn); |
| pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); |
| pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive); |
| pw.print(prefix); pw.print("mLandscapeRotation="); |
| pw.print(Surface.rotationToString(mLandscapeRotation)); |
| pw.print(" mSeascapeRotation="); |
| pw.println(Surface.rotationToString(mSeascapeRotation)); |
| pw.print(prefix); pw.print("mPortraitRotation="); |
| pw.print(Surface.rotationToString(mPortraitRotation)); |
| pw.print(" mUpsideDownRotation="); |
| pw.println(Surface.rotationToString(mUpsideDownRotation)); |
| pw.print(prefix); pw.print("mDemoHdmiRotation="); |
| pw.print(Surface.rotationToString(mDemoHdmiRotation)); |
| pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock); |
| pw.print(prefix); pw.print("mUndockedHdmiRotation="); |
| pw.println(Surface.rotationToString(mUndockedHdmiRotation)); |
| if (mHasFeatureLeanback) { |
| pw.print(prefix); |
| pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed); |
| pw.print(prefix); |
| pw.print("mAccessibilityTvKey2Pressed="); pw.println(mAccessibilityTvKey2Pressed); |
| pw.print(prefix); |
| pw.print("mAccessibilityTvScheduled="); pw.println(mAccessibilityTvScheduled); |
| } |
| |
| mGlobalKeyManager.dump(prefix, pw); |
| mStatusBarController.dump(pw, prefix); |
| mNavigationBarController.dump(pw, prefix); |
| PolicyControl.dump(prefix, pw); |
| |
| if (mWakeGestureListener != null) { |
| mWakeGestureListener.dump(pw, prefix); |
| } |
| if (mOrientationListener != null) { |
| mOrientationListener.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 allowAllRotationsToString(int allowAll) { |
| switch (allowAll) { |
| case -1: |
| return "unknown"; |
| case 0: |
| return "false"; |
| case 1: |
| return "true"; |
| default: |
| return Integer.toString(allowAll); |
| } |
| } |
| |
| 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"; |
| 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"; |
| 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"; |
| 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 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"; |
| 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); |
| } |
| } |
| |
| @Override |
| public void onLockTaskStateChangedLw(int lockTaskState) { |
| mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState); |
| } |
| |
| @Override |
| public boolean setAodShowing(boolean aodShowing) { |
| if (mAodShowing != aodShowing) { |
| mAodShowing = aodShowing; |
| return true; |
| } |
| return false; |
| } |
| } |