| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wm; |
| |
| import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; |
| import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; |
| import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; |
| import static android.Manifest.permission.MANAGE_APP_TOKENS; |
| import static android.Manifest.permission.READ_FRAME_BUFFER; |
| import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; |
| import static android.Manifest.permission.RESTRICTED_VR_ACCESS; |
| import static android.Manifest.permission.WRITE_SECURE_SETTINGS; |
| import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; |
| import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; |
| import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; |
| import static android.app.StatusBarManager.DISABLE_MASK; |
| import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; |
| import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; |
| import static android.content.pm.PackageManager.FEATURE_PC; |
| import static android.content.pm.PackageManager.PERMISSION_GRANTED; |
| import static android.os.Process.SYSTEM_UID; |
| import static android.os.Process.myPid; |
| import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; |
| import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; |
| import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; |
| import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; |
| import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; |
| import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| import static android.view.Display.INVALID_DISPLAY; |
| import static android.view.WindowManager.DOCKED_INVALID; |
| import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; |
| import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; |
| import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; |
| import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; |
| import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; |
| import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| import static android.view.WindowManager.LayoutParams.FLAG_SECURE; |
| 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.INPUT_FEATURE_NO_INPUT_CHANNEL; |
| import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; |
| import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; |
| import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; |
| 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_METHOD; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; |
| 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_STATUS_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_TOAST; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; |
| import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; |
| import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; |
| import static android.view.WindowManagerGlobal.ADD_OKAY; |
| import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; |
| import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; |
| import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; |
| |
| import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; |
| import static com.android.server.LockGuard.INDEX_WINDOW; |
| import static com.android.server.LockGuard.installLock; |
| import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_BOOT; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; |
| import static com.android.server.wm.ProtoLogGroup.WM_ERROR; |
| import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; |
| import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; |
| import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; |
| import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; |
| import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; |
| import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; |
| import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.DISPLAY_FROZEN; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_APP; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_DISPLAY_ID; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.LAST_ORIENTATION; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER; |
| import static com.android.server.wm.WindowManagerServiceDumpProto.ROTATION; |
| |
| import android.Manifest; |
| import android.Manifest.permission; |
| import android.animation.ValueAnimator; |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.RequiresPermission; |
| import android.app.ActivityManager; |
| import android.app.ActivityManager.TaskSnapshot; |
| import android.app.ActivityManagerInternal; |
| import android.app.ActivityTaskManager; |
| import android.app.ActivityThread; |
| import android.app.AppOpsManager; |
| import android.app.IActivityManager; |
| import android.app.IActivityTaskManager; |
| import android.app.IAssistDataReceiver; |
| import android.app.WindowConfiguration; |
| import android.app.admin.DevicePolicyCache; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManagerInternal; |
| import android.content.res.Configuration; |
| import android.content.res.TypedArray; |
| import android.database.ContentObserver; |
| import android.graphics.Bitmap; |
| import android.graphics.Insets; |
| import android.graphics.Matrix; |
| import android.graphics.Point; |
| import android.graphics.Rect; |
| import android.graphics.RectF; |
| import android.graphics.Region; |
| import android.hardware.configstore.V1_0.ISurfaceFlingerConfigs; |
| import android.hardware.configstore.V1_0.OptionalBool; |
| import android.hardware.display.DisplayManager; |
| import android.hardware.display.DisplayManagerInternal; |
| import android.hardware.input.InputManager; |
| import android.hardware.input.InputManagerInternal; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Debug; |
| import android.os.Handler; |
| import android.os.HandlerExecutor; |
| import android.os.IBinder; |
| import android.os.IRemoteCallback; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Parcel; |
| import android.os.ParcelFileDescriptor; |
| import android.os.PowerManager; |
| import android.os.PowerManager.ServiceType; |
| import android.os.PowerManagerInternal; |
| import android.os.PowerSaveState; |
| import android.os.RemoteException; |
| import android.os.ResultReceiver; |
| import android.os.ServiceManager; |
| import android.os.ShellCallback; |
| import android.os.StrictMode; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.SystemService; |
| import android.os.Trace; |
| import android.os.UserHandle; |
| import android.os.WorkSource; |
| import android.provider.Settings; |
| import android.service.vr.IVrManager; |
| import android.service.vr.IVrStateCallbacks; |
| import android.text.format.DateUtils; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.DisplayMetrics; |
| import android.util.Log; |
| import android.util.MergedConfiguration; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.SparseBooleanArray; |
| import android.util.TimeUtils; |
| import android.util.TypedValue; |
| import android.util.proto.ProtoOutputStream; |
| import android.view.Choreographer; |
| import android.view.Display; |
| import android.view.DisplayCutout; |
| import android.view.DisplayInfo; |
| import android.view.Gravity; |
| import android.view.IAppTransitionAnimationSpecsFuture; |
| import android.view.IDisplayFoldListener; |
| import android.view.IDisplayWindowInsetsController; |
| import android.view.IDisplayWindowListener; |
| import android.view.IDisplayWindowRotationController; |
| import android.view.IDockedStackListener; |
| import android.view.IInputFilter; |
| import android.view.IOnKeyguardExitResult; |
| import android.view.IPinnedStackListener; |
| import android.view.IRecentsAnimationRunner; |
| import android.view.IRotationWatcher; |
| import android.view.ISystemGestureExclusionListener; |
| import android.view.IWallpaperVisibilityListener; |
| import android.view.IWindow; |
| import android.view.IWindowId; |
| import android.view.IWindowManager; |
| import android.view.IWindowSession; |
| import android.view.IWindowSessionCallback; |
| import android.view.InputApplicationHandle; |
| import android.view.InputChannel; |
| import android.view.InputDevice; |
| import android.view.InputEvent; |
| import android.view.InputEventReceiver; |
| import android.view.InputWindowHandle; |
| import android.view.InsetsState; |
| import android.view.KeyEvent; |
| import android.view.MagnificationSpec; |
| import android.view.MotionEvent; |
| import android.view.PointerIcon; |
| import android.view.RemoteAnimationAdapter; |
| import android.view.Surface; |
| import android.view.SurfaceControl; |
| import android.view.SurfaceSession; |
| import android.view.View; |
| import android.view.WindowContentFrameStats; |
| import android.view.WindowInsets; |
| import android.view.WindowManager; |
| import android.view.WindowManager.LayoutParams; |
| import android.view.WindowManager.RemoveContentMode; |
| import android.view.WindowManager.TransitionType; |
| import android.view.WindowManagerGlobal; |
| import android.view.WindowManagerPolicyConstants.PointerEventListener; |
| |
| import com.android.internal.R; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.internal.os.IResultReceiver; |
| import com.android.internal.policy.IKeyguardDismissCallback; |
| import com.android.internal.policy.IShortcutService; |
| import com.android.internal.policy.KeyInterceptionInfo; |
| import com.android.internal.util.DumpUtils; |
| import com.android.internal.util.FastPrintWriter; |
| import com.android.internal.util.LatencyTracker; |
| import com.android.internal.util.function.pooled.PooledConsumer; |
| import com.android.internal.util.function.pooled.PooledLambda; |
| import com.android.internal.view.WindowManagerPolicyThread; |
| import com.android.server.AnimationThread; |
| import com.android.server.DisplayThread; |
| import com.android.server.FgThread; |
| import com.android.server.LocalServices; |
| import com.android.server.UiThread; |
| import com.android.server.Watchdog; |
| import com.android.server.input.InputManagerService; |
| import com.android.server.policy.WindowManagerPolicy; |
| import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; |
| import com.android.server.power.ShutdownThread; |
| import com.android.server.protolog.ProtoLogImpl; |
| import com.android.server.protolog.common.ProtoLog; |
| import com.android.server.utils.PriorityDump; |
| import com.android.server.wm.utils.DeviceConfigInterface; |
| |
| import java.io.BufferedWriter; |
| import java.io.DataInputStream; |
| import java.io.File; |
| import java.io.FileDescriptor; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.net.Socket; |
| import java.text.DateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.function.Function; |
| import java.util.function.Supplier; |
| |
| /** {@hide} */ |
| public class WindowManagerService extends IWindowManager.Stub |
| implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM; |
| |
| static final int LAYOUT_REPEAT_THRESHOLD = 4; |
| |
| static final boolean PROFILE_ORIENTATION = false; |
| |
| /** How much to multiply the policy's type layer, to reserve room |
| * for multiple windows of the same type and Z-ordering adjustment |
| * with TYPE_LAYER_OFFSET. */ |
| static final int TYPE_LAYER_MULTIPLIER = 10000; |
| |
| /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above |
| * or below others in the same layer. */ |
| static final int TYPE_LAYER_OFFSET = 1000; |
| |
| /** How much to increment the layer for each window, to reserve room |
| * for effect surfaces between them. |
| */ |
| static final int WINDOW_LAYER_MULTIPLIER = 5; |
| |
| /** |
| * Animation thumbnail is as far as possible below the window above |
| * the thumbnail (or in other words as far as possible above the window |
| * below it). |
| */ |
| static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1; |
| |
| /** The maximum length we will accept for a loaded animation duration: |
| * this is 10 seconds. |
| */ |
| static final int MAX_ANIMATION_DURATION = 10 * 1000; |
| |
| /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */ |
| static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000; |
| |
| /** Amount of time (in milliseconds) to delay before declaring a seamless rotation timeout. */ |
| static final int SEAMLESS_ROTATION_TIMEOUT_DURATION = 2000; |
| |
| /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */ |
| static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000; |
| |
| /** Amount of time to allow a last ANR message to exist before freeing the memory. */ |
| static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours |
| |
| // Maximum number of milliseconds to wait for input devices to be enumerated before |
| // proceding with safe mode detection. |
| private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; |
| |
| // Default input dispatching timeout in nanoseconds. |
| static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; |
| |
| // Poll interval in milliseconds for watching boot animation finished. |
| private static final int BOOT_ANIMATION_POLL_INTERVAL = 200; |
| |
| // The name of the boot animation service in init.rc. |
| private static final String BOOT_ANIMATION_SERVICE = "bootanim"; |
| |
| static final int UPDATE_FOCUS_NORMAL = 0; |
| static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; |
| static final int UPDATE_FOCUS_PLACING_SURFACES = 2; |
| static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; |
| /** Indicates we are removing the focused window when updating the focus. */ |
| static final int UPDATE_FOCUS_REMOVING_FOCUS = 4; |
| |
| private static final String SYSTEM_SECURE = "ro.secure"; |
| private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; |
| |
| private static final String DENSITY_OVERRIDE = "ro.config.density_override"; |
| private static final String SIZE_OVERRIDE = "ro.config.size_override"; |
| |
| private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; |
| |
| // Used to indicate that if there is already a transition set, it should be preserved when |
| // trying to apply a new one. |
| private static final boolean ALWAYS_KEEP_CURRENT = true; |
| |
| /** |
| * If set, new app transition framework which supports setting animation on any element |
| * in a surface is used. |
| * <p> |
| * Only set this to non-zero once the new app transition framework is productionalized. |
| * </p> |
| */ |
| private static final String HIERARCHICAL_ANIMATIONS_PROPERTY = |
| "persist.wm.hierarchical_animations"; |
| |
| /** |
| * @see #HIERARCHICAL_ANIMATIONS_PROPERTY |
| */ |
| static boolean sHierarchicalAnimations = |
| SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, true); |
| |
| // Enums for animation scale update types. |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE}) |
| private @interface UpdateAnimationScaleMode {}; |
| private static final int WINDOW_ANIMATION_SCALE = 0; |
| private static final int TRANSITION_ANIMATION_SCALE = 1; |
| private static final int ANIMATION_DURATION_SCALE = 2; |
| |
| private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; |
| |
| final WindowManagerConstants mConstants; |
| |
| final WindowTracing mWindowTracing; |
| |
| final private KeyguardDisableHandler mKeyguardDisableHandler; |
| // TODO: eventually unify all keyguard state in a common place instead of having it spread over |
| // AM's KeyguardController and the policy's KeyguardServiceDelegate. |
| boolean mKeyguardGoingAway; |
| boolean mKeyguardOrAodShowingOnDefaultDisplay; |
| // VR Vr2d Display Id. |
| int mVr2dDisplayId = INVALID_DISPLAY; |
| boolean mVrModeEnabled = false; |
| |
| /* If true, shadows drawn around the window will be rendered by the system compositor. If |
| * false, shadows will be drawn by the client by setting an elevation on the root view and |
| * the contents will be inset by the shadow radius. */ |
| boolean mRenderShadowsInCompositor = false; |
| |
| /** |
| * Tracks a map of input tokens to info that is used to decide whether to intercept |
| * a key event. |
| */ |
| final Map<IBinder, KeyInterceptionInfo> mKeyInterceptionInfoForToken = |
| Collections.synchronizedMap(new ArrayMap<>()); |
| |
| |
| private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { |
| @Override |
| public void onVrStateChanged(boolean enabled) { |
| synchronized (mGlobalLock) { |
| mVrModeEnabled = enabled; |
| final PooledConsumer c = PooledLambda.obtainConsumer( |
| DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled); |
| mRoot.forAllDisplayPolicies(c); |
| c.recycle(); |
| } |
| } |
| }; |
| |
| private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| switch (intent.getAction()) { |
| case ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED: |
| mKeyguardDisableHandler.updateKeyguardEnabled(getSendingUserId()); |
| break; |
| } |
| } |
| }; |
| final WindowSurfacePlacer mWindowPlacerLocked; |
| |
| private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() { |
| @Override |
| public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, |
| boolean asProto) { |
| // Bugreport dumps the trace 2x, 1x as proto and 1x as text. Save file to disk only 1x. |
| if (asProto && mWindowTracing.isEnabled()) { |
| mWindowTracing.stopTrace(null, false /* writeToFile */); |
| BackgroundThread.getHandler().post(() -> { |
| mWindowTracing.writeTraceToFile(); |
| mWindowTracing.startTrace(null); |
| }); |
| } |
| doDump(fd, pw, new String[] {"-a"}, asProto); |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { |
| doDump(fd, pw, args, asProto); |
| } |
| }; |
| |
| /** |
| * Current user when multi-user is enabled. Don't show windows of |
| * non-current user. Also see mCurrentProfileIds. |
| */ |
| int mCurrentUserId; |
| /** |
| * Users that are profiles of the current user. These are also allowed to show windows |
| * on the current user. |
| */ |
| int[] mCurrentProfileIds = new int[] {}; |
| |
| final Context mContext; |
| |
| final boolean mHasPermanentDpad; |
| final long mDrawLockTimeoutMillis; |
| final boolean mAllowAnimationsInLowPowerMode; |
| |
| final boolean mAllowBootMessages; |
| |
| final boolean mLimitedAlphaCompositing; |
| final int mMaxUiWidth; |
| |
| @VisibleForTesting |
| WindowManagerPolicy mPolicy; |
| |
| final IActivityManager mActivityManager; |
| // TODO: Probably not needed once activities are fully in WM. |
| final IActivityTaskManager mActivityTaskManager; |
| final ActivityManagerInternal mAmInternal; |
| final ActivityTaskManagerInternal mAtmInternal; |
| |
| final AppOpsManager mAppOps; |
| final PackageManagerInternal mPmInternal; |
| |
| final DisplayWindowSettings mDisplayWindowSettings; |
| |
| /** If the system should display notifications for apps displaying an alert window. */ |
| boolean mShowAlertWindowNotifications = true; |
| |
| /** |
| * All currently active sessions with clients. |
| */ |
| final ArraySet<Session> mSessions = new ArraySet<>(); |
| |
| /** Mapping from an IWindow IBinder to the server's Window object. */ |
| final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>(); |
| |
| /** Mapping from an InputWindowHandle token to the server's Window object. */ |
| final HashMap<IBinder, WindowState> mInputToWindowMap = new HashMap<>(); |
| |
| /** Global service lock used by the package the owns this service. */ |
| final WindowManagerGlobalLock mGlobalLock; |
| |
| /** |
| * List of app window tokens that are waiting for replacing windows. If the |
| * replacement doesn't come in time the stale windows needs to be disposed of. |
| */ |
| final ArrayList<ActivityRecord> mWindowReplacementTimeouts = new ArrayList<>(); |
| |
| /** |
| * Windows that are being resized. Used so we can tell the client about |
| * the resize after closing the transaction in which we resized the |
| * underlying surface. |
| */ |
| final ArrayList<WindowState> mResizingWindows = new ArrayList<>(); |
| |
| /** |
| * Windows whose animations have ended and now must be removed. |
| */ |
| final ArrayList<WindowState> mPendingRemove = new ArrayList<>(); |
| |
| /** |
| * Used when processing mPendingRemove to avoid working on the original array. |
| */ |
| WindowState[] mPendingRemoveTmp = new WindowState[20]; |
| |
| // TODO: use WindowProcessController once go/wm-unified is done. |
| /** Mapping of process pids to configurations */ |
| final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>(); |
| |
| /** |
| * Windows whose surface should be destroyed. |
| */ |
| final ArrayList<WindowState> mDestroySurface = new ArrayList<>(); |
| |
| /** |
| * Windows with a preserved surface waiting to be destroyed. These windows |
| * are going through a surface change. We keep the old surface around until |
| * the first frame on the new surface finishes drawing. |
| */ |
| final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>(); |
| |
| /** |
| * This is set when we have run out of memory, and will either be an empty |
| * list or contain windows that need to be force removed. |
| */ |
| final ArrayList<WindowState> mForceRemoves = new ArrayList<>(); |
| |
| /** |
| * The callbacks to make when the windows all have been drawn for a given |
| * {@link WindowContainer}. |
| */ |
| final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>(); |
| |
| /** List of window currently causing non-system overlay windows to be hidden. */ |
| private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>(); |
| |
| AccessibilityController mAccessibilityController; |
| private RecentsAnimationController mRecentsAnimationController; |
| |
| Watermark mWatermark; |
| StrictModeFlash mStrictModeFlash; |
| EmulatorDisplayOverlay mEmulatorDisplayOverlay; |
| |
| final float[] mTmpFloats = new float[9]; |
| final Rect mTmpRect = new Rect(); |
| final Rect mTmpRect2 = new Rect(); |
| final Rect mTmpRect3 = new Rect(); |
| final RectF mTmpRectF = new RectF(); |
| |
| final Matrix mTmpTransform = new Matrix(); |
| |
| boolean mDisplayReady; |
| boolean mSafeMode; |
| boolean mDisplayEnabled = false; |
| boolean mSystemBooted = false; |
| boolean mForceDisplayEnabled = false; |
| boolean mShowingBootMessages = false; |
| boolean mBootAnimationStopped = false; |
| boolean mSystemReady = false; |
| |
| // Following variables are for debugging screen wakelock only. |
| WindowState mLastWakeLockHoldingWindow = null; |
| WindowState mLastWakeLockObscuringWindow = null; |
| |
| /** Dump of the windows and app tokens at the time of the last ANR. Cleared after |
| * LAST_ANR_LIFETIME_DURATION_MSECS */ |
| String mLastANRState; |
| |
| // The root of the device window hierarchy. |
| RootWindowContainer mRoot; |
| |
| int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; |
| Rect mDockedStackCreateBounds; |
| |
| boolean mIsPc; |
| /** |
| * Flag that indicates that desktop mode is forced for public secondary screens. |
| * |
| * This includes several settings: |
| * - Set freeform windowing mode on external screen if it's supported and enabled. |
| * - Enable system decorations and IME on external screen. |
| * - TODO: Show mouse pointer on external screen. |
| */ |
| boolean mForceDesktopModeOnExternalDisplays; |
| |
| boolean mDisableTransitionAnimation; |
| |
| class RotationWatcher { |
| final IRotationWatcher mWatcher; |
| final IBinder.DeathRecipient mDeathRecipient; |
| final int mDisplayId; |
| RotationWatcher(IRotationWatcher watcher, IBinder.DeathRecipient deathRecipient, |
| int displayId) { |
| mWatcher = watcher; |
| mDeathRecipient = deathRecipient; |
| mDisplayId = displayId; |
| } |
| } |
| |
| ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<>(); |
| final WallpaperVisibilityListeners mWallpaperVisibilityListeners = |
| new WallpaperVisibilityListeners(); |
| |
| IDisplayWindowRotationController mDisplayRotationController = null; |
| private final DeathRecipient mDisplayRotationControllerDeath = |
| () -> mDisplayRotationController = null; |
| |
| final DisplayWindowListenerController mDisplayNotificationController; |
| |
| boolean mDisplayFrozen = false; |
| long mDisplayFreezeTime = 0; |
| int mLastDisplayFreezeDuration = 0; |
| Object mLastFinishedFreezeSource = null; |
| boolean mSwitchingUser = false; |
| |
| final static int WINDOWS_FREEZING_SCREENS_NONE = 0; |
| final static int WINDOWS_FREEZING_SCREENS_ACTIVE = 1; |
| final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2; |
| int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; |
| |
| boolean mClientFreezingScreen = false; |
| int mAppsFreezingScreen = 0; |
| |
| @VisibleForTesting |
| boolean mPerDisplayFocusEnabled; |
| |
| // State while inside of layoutAndPlaceSurfacesLocked(). |
| boolean mFocusMayChange; |
| |
| // This is held as long as we have the screen frozen, to give us time to |
| // perform a rotation animation when turning off shows the lock screen which |
| // changes the orientation. |
| private final PowerManager.WakeLock mScreenFrozenLock; |
| |
| final TaskSnapshotController mTaskSnapshotController; |
| |
| boolean mIsTouchDevice; |
| |
| final H mH = new H(); |
| |
| /** |
| * Handler for things to run that have direct impact on an animation, i.e. animation tick, |
| * layout, starting window creation, whereas {@link H} runs things that are still important, but |
| * not as critical. |
| */ |
| final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper()); |
| |
| boolean mHardKeyboardAvailable; |
| WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; |
| SettingsObserver mSettingsObserver; |
| final EmbeddedWindowController mEmbeddedWindowController; |
| |
| @VisibleForTesting |
| final class SettingsObserver extends ContentObserver { |
| private final Uri mDisplayInversionEnabledUri = |
| Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); |
| private final Uri mWindowAnimationScaleUri = |
| Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE); |
| private final Uri mTransitionAnimationScaleUri = |
| Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); |
| private final Uri mAnimationDurationScaleUri = |
| Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE); |
| private final Uri mImmersiveModeConfirmationsUri = |
| Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS); |
| private final Uri mPolicyControlUri = |
| Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL); |
| private final Uri mPointerLocationUri = |
| Settings.System.getUriFor(Settings.System.POINTER_LOCATION); |
| private final Uri mForceDesktopModeOnExternalDisplaysUri = Settings.Global.getUriFor( |
| Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS); |
| private final Uri mFreeformWindowUri = Settings.Global.getUriFor( |
| Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT); |
| private final Uri mForceResizableUri = Settings.Global.getUriFor( |
| DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES); |
| private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor( |
| DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM); |
| private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor( |
| DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR); |
| |
| public SettingsObserver() { |
| super(new Handler()); |
| ContentResolver resolver = mContext.getContentResolver(); |
| resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mWindowAnimationScaleUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mTransitionAnimationScaleUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mAnimationDurationScaleUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mPolicyControlUri, false, this, UserHandle.USER_ALL); |
| resolver.registerContentObserver(mPointerLocationUri, false, this, UserHandle.USER_ALL); |
| resolver.registerContentObserver(mForceDesktopModeOnExternalDisplaysUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL); |
| resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL); |
| resolver.registerContentObserver(mSizeCompatFreeformUri, false, this, |
| UserHandle.USER_ALL); |
| resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this, |
| UserHandle.USER_ALL); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange, Uri uri) { |
| if (uri == null) { |
| return; |
| } |
| |
| if (mImmersiveModeConfirmationsUri.equals(uri) || mPolicyControlUri.equals(uri)) { |
| updateSystemUiSettings(); |
| return; |
| } |
| |
| if (mPointerLocationUri.equals(uri)) { |
| updatePointerLocation(); |
| return; |
| } |
| |
| if (mForceDesktopModeOnExternalDisplaysUri.equals(uri)) { |
| updateForceDesktopModeOnExternalDisplays(); |
| return; |
| } |
| |
| if (mFreeformWindowUri.equals(uri)) { |
| updateFreeformWindowManagement(); |
| return; |
| } |
| |
| if (mForceResizableUri.equals(uri)) { |
| updateForceResizableTasks(); |
| return; |
| } |
| |
| if (mSizeCompatFreeformUri.equals(uri)) { |
| updateSizeCompatFreeform(); |
| return; |
| } |
| |
| if (mRenderShadowsInCompositorUri.equals(uri)) { |
| setShadowRenderer(); |
| return; |
| } |
| |
| @UpdateAnimationScaleMode |
| final int mode; |
| if (mWindowAnimationScaleUri.equals(uri)) { |
| mode = WINDOW_ANIMATION_SCALE; |
| } else if (mTransitionAnimationScaleUri.equals(uri)) { |
| mode = TRANSITION_ANIMATION_SCALE; |
| } else if (mAnimationDurationScaleUri.equals(uri)) { |
| mode = ANIMATION_DURATION_SCALE; |
| } else { |
| // Ignoring unrecognized content changes |
| return; |
| } |
| Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0); |
| mH.sendMessage(m); |
| } |
| |
| void updateSystemUiSettings() { |
| boolean changed; |
| synchronized (mGlobalLock) { |
| changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext) |
| || PolicyControl.reloadFromSetting(mContext); |
| } |
| if (changed) { |
| updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */); |
| } |
| } |
| |
| void updatePointerLocation() { |
| ContentResolver resolver = mContext.getContentResolver(); |
| final boolean enablePointerLocation = Settings.System.getIntForUser(resolver, |
| Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT) != 0; |
| |
| if (mPointerLocationEnabled == enablePointerLocation) { |
| return; |
| } |
| mPointerLocationEnabled = enablePointerLocation; |
| synchronized (mGlobalLock) { |
| final PooledConsumer c = PooledLambda.obtainConsumer( |
| DisplayPolicy::setPointerLocationEnabled, PooledLambda.__(), |
| mPointerLocationEnabled); |
| mRoot.forAllDisplayPolicies(c); |
| c.recycle(); |
| } |
| } |
| |
| void updateForceDesktopModeOnExternalDisplays() { |
| ContentResolver resolver = mContext.getContentResolver(); |
| final boolean enableForceDesktopMode = Settings.Global.getInt(resolver, |
| DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; |
| if (mForceDesktopModeOnExternalDisplays == enableForceDesktopMode) { |
| return; |
| } |
| setForceDesktopModeOnExternalDisplays(enableForceDesktopMode); |
| } |
| |
| void updateFreeformWindowManagement() { |
| ContentResolver resolver = mContext.getContentResolver(); |
| final boolean freeformWindowManagement = mContext.getPackageManager().hasSystemFeature( |
| FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt( |
| resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; |
| |
| mAtmService.mSupportsFreeformWindowManagement = freeformWindowManagement; |
| } |
| |
| void updateForceResizableTasks() { |
| ContentResolver resolver = mContext.getContentResolver(); |
| final boolean forceResizable = Settings.Global.getInt(resolver, |
| DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; |
| |
| mAtmService.mForceResizableActivities = forceResizable; |
| } |
| |
| void updateSizeCompatFreeform() { |
| ContentResolver resolver = mContext.getContentResolver(); |
| final boolean sizeCompatFreeform = Settings.Global.getInt(resolver, |
| DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0; |
| |
| mAtmService.mSizeCompatFreeform = sizeCompatFreeform; |
| } |
| } |
| |
| private void setShadowRenderer() { |
| mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(), |
| DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; |
| } |
| |
| PowerManager mPowerManager; |
| PowerManagerInternal mPowerManagerInternal; |
| |
| private float mWindowAnimationScaleSetting = 1.0f; |
| private float mTransitionAnimationScaleSetting = 1.0f; |
| private float mAnimatorDurationScaleSetting = 1.0f; |
| private boolean mAnimationsDisabled = false; |
| boolean mPointerLocationEnabled = false; |
| |
| final InputManagerService mInputManager; |
| final DisplayManagerInternal mDisplayManagerInternal; |
| final DisplayManager mDisplayManager; |
| final ActivityTaskManagerService mAtmService; |
| |
| /** Indicates whether this device supports wide color gamut / HDR rendering */ |
| private boolean mHasWideColorGamutSupport; |
| private boolean mHasHdrSupport; |
| |
| /** Who is holding the screen on. */ |
| private Session mHoldingScreenOn; |
| private PowerManager.WakeLock mHoldingScreenWakeLock; |
| |
| /** Whether or not a layout can cause a wake up when theater mode is enabled. */ |
| boolean mAllowTheaterModeWakeFromLayout; |
| |
| final TaskPositioningController mTaskPositioningController; |
| final DragDropController mDragDropController; |
| |
| /** For frozen screen animations. */ |
| private int mExitAnimId, mEnterAnimId; |
| |
| /** The display that the rotation animation is applying to. */ |
| private int mFrozenDisplayId; |
| |
| /** Skip repeated ActivityRecords initialization. Note that AppWindowsToken's version of this |
| * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ |
| int mTransactionSequence; |
| |
| final WindowAnimator mAnimator; |
| SurfaceAnimationRunner mSurfaceAnimationRunner; |
| |
| /** |
| * Keeps track of which animations got transferred to which animators. Entries will get cleaned |
| * up when the animation finishes. |
| */ |
| final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>(); |
| |
| private WindowContentFrameStats mTempWindowRenderStats; |
| |
| private final LatencyTracker mLatencyTracker; |
| |
| /** |
| * Whether the UI is currently running in touch mode (not showing |
| * navigational focus because the user is directly pressing the screen). |
| */ |
| private boolean mInTouchMode; |
| |
| private ViewServer mViewServer; |
| final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>(); |
| boolean mWindowsChanged = false; |
| |
| public interface WindowChangeListener { |
| public void windowsChanged(); |
| public void focusChanged(); |
| } |
| |
| final Configuration mTempConfiguration = new Configuration(); |
| |
| final HighRefreshRateBlacklist mHighRefreshRateBlacklist; |
| |
| // If true, only the core apps and services are being launched because the device |
| // is in a special boot mode, such as being encrypted or waiting for a decryption password. |
| // For example, when this flag is true, there will be no wallpaper service. |
| final boolean mOnlyCore; |
| |
| static WindowManagerThreadPriorityBooster sThreadPriorityBooster = |
| new WindowManagerThreadPriorityBooster(); |
| |
| Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory; |
| Supplier<SurfaceControl.Transaction> mTransactionFactory; |
| final Supplier<Surface> mSurfaceFactory; |
| |
| private final SurfaceControl.Transaction mTransaction; |
| |
| static void boostPriorityForLockedSection() { |
| sThreadPriorityBooster.boost(); |
| } |
| |
| static void resetPriorityAfterLockedSection() { |
| sThreadPriorityBooster.reset(); |
| } |
| |
| void openSurfaceTransaction() { |
| try { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction"); |
| synchronized (mGlobalLock) { |
| SurfaceControl.openTransaction(); |
| } |
| } finally { |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| } |
| |
| /** |
| * Closes a surface transaction. |
| * @param where debug string indicating where the transaction originated |
| */ |
| void closeSurfaceTransaction(String where) { |
| try { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction"); |
| synchronized (mGlobalLock) { |
| SurfaceControl.closeTransaction(); |
| mWindowTracing.logState(where); |
| } |
| } finally { |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| } |
| |
| /** Listener to notify activity manager about app transitions. */ |
| final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier |
| = new WindowManagerInternal.AppTransitionListener() { |
| |
| @Override |
| public void onAppTransitionCancelledLocked(int transit) { |
| mAtmInternal.notifyAppTransitionCancelled(); |
| } |
| |
| @Override |
| public void onAppTransitionFinishedLocked(IBinder token) { |
| mAtmInternal.notifyAppTransitionFinished(); |
| final ActivityRecord atoken = mRoot.getActivityRecord(token); |
| if (atoken == null) { |
| return; |
| } |
| |
| // While running a recents animation, this will get called early because we show the |
| // recents animation target activity immediately when the animation starts. Defer the |
| // mLaunchTaskBehind updates until recents animation finishes. |
| final boolean isRecentsAnimationTarget = getRecentsAnimationController() != null |
| && getRecentsAnimationController().isTargetApp(atoken); |
| if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget) { |
| try { |
| mActivityTaskManager.notifyLaunchTaskBehindComplete(atoken.token); |
| } catch (RemoteException e) { |
| } |
| atoken.mLaunchTaskBehind = false; |
| } else { |
| atoken.updateReportedVisibilityLocked(); |
| // We should also defer sending the finished callback until the recents animation |
| // successfully finishes. |
| if (atoken.mEnteringAnimation && !isRecentsAnimationTarget) { |
| atoken.mEnteringAnimation = false; |
| try { |
| mActivityTaskManager.notifyEnterAnimationComplete(atoken.token); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| } |
| }; |
| |
| final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>(); |
| |
| interface AppFreezeListener { |
| void onAppFreezeTimeout(); |
| } |
| |
| private static WindowManagerService sInstance; |
| static WindowManagerService getInstance() { |
| return sInstance; |
| } |
| |
| public static WindowManagerService main(final Context context, final InputManagerService im, |
| final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, |
| ActivityTaskManagerService atm) { |
| return main(context, im, showBootMsgs, onlyCore, policy, atm, |
| SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new); |
| } |
| |
| /** |
| * Creates and returns an instance of the WindowManagerService. This call allows the caller |
| * to override factories that can be used to stub native calls during test. |
| */ |
| @VisibleForTesting |
| public static WindowManagerService main(final Context context, final InputManagerService im, |
| final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, |
| ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory, |
| Supplier<Surface> surfaceFactory, |
| Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { |
| DisplayThread.getHandler().runWithScissors(() -> |
| sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, |
| atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0); |
| return sInstance; |
| } |
| |
| private void initPolicy() { |
| UiThread.getHandler().runWithScissors(new Runnable() { |
| @Override |
| public void run() { |
| WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); |
| mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); |
| } |
| }, 0); |
| } |
| |
| @Override |
| public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, |
| String[] args, ShellCallback callback, ResultReceiver result) { |
| new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result); |
| } |
| |
| private WindowManagerService(Context context, InputManagerService inputManager, |
| boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, |
| ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory, |
| Supplier<Surface> surfaceFactory, |
| Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { |
| installLock(this, INDEX_WINDOW); |
| mGlobalLock = atm.getGlobalLock(); |
| mAtmService = atm; |
| mContext = context; |
| mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); |
| mAllowBootMessages = showBootMsgs; |
| mOnlyCore = onlyCore; |
| mLimitedAlphaCompositing = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_sf_limitedAlpha); |
| mHasPermanentDpad = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_hasPermanentDpad); |
| mInTouchMode = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_defaultInTouchMode); |
| inputManager.setInTouchMode(mInTouchMode); |
| mDrawLockTimeoutMillis = context.getResources().getInteger( |
| com.android.internal.R.integer.config_drawLockTimeoutMillis); |
| mAllowAnimationsInLowPowerMode = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_allowAnimationsInLowPowerMode); |
| mMaxUiWidth = context.getResources().getInteger( |
| com.android.internal.R.integer.config_maxUiWidth); |
| mDisableTransitionAnimation = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_disableTransitionAnimation); |
| mPerDisplayFocusEnabled = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_perDisplayFocusEnabled); |
| mInputManager = inputManager; // Must be before createDisplayContentLocked. |
| mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); |
| |
| mSurfaceControlFactory = surfaceControlFactory; |
| mTransactionFactory = transactionFactory; |
| mSurfaceFactory = surfaceFactory; |
| mTransaction = mTransactionFactory.get(); |
| |
| mDisplayWindowSettings = new DisplayWindowSettings(this); |
| mPolicy = policy; |
| mAnimator = new WindowAnimator(this); |
| mRoot = new RootWindowContainer(this); |
| |
| mWindowPlacerLocked = new WindowSurfacePlacer(this); |
| mTaskSnapshotController = new TaskSnapshotController(this); |
| |
| mWindowTracing = WindowTracing.createDefaultAndStartLooper(this, |
| Choreographer.getInstance()); |
| |
| LocalServices.addService(WindowManagerPolicy.class, mPolicy); |
| |
| mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); |
| |
| mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH); |
| |
| mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); |
| mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); |
| |
| if (mPowerManagerInternal != null) { |
| mPowerManagerInternal.registerLowPowerModeObserver( |
| new PowerManagerInternal.LowPowerModeListener() { |
| @Override |
| public int getServiceType() { |
| return ServiceType.ANIMATION; |
| } |
| |
| @Override |
| public void onLowPowerModeChanged(PowerSaveState result) { |
| synchronized (mGlobalLock) { |
| final boolean enabled = result.batterySaverEnabled; |
| if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) { |
| mAnimationsDisabled = enabled; |
| dispatchNewAnimatorScaleLocked(null); |
| } |
| } |
| } |
| }); |
| mAnimationsDisabled = mPowerManagerInternal |
| .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled; |
| } |
| mScreenFrozenLock = mPowerManager.newWakeLock( |
| PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); |
| mScreenFrozenLock.setReferenceCounted(false); |
| |
| mDisplayNotificationController = new DisplayWindowListenerController(this); |
| |
| mActivityManager = ActivityManager.getService(); |
| mActivityTaskManager = ActivityTaskManager.getService(); |
| mAmInternal = LocalServices.getService(ActivityManagerInternal.class); |
| mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); |
| mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); |
| AppOpsManager.OnOpChangedInternalListener opListener = |
| new AppOpsManager.OnOpChangedInternalListener() { |
| @Override public void onOpChanged(int op, String packageName) { |
| updateAppOpsState(); |
| } |
| }; |
| mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener); |
| mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener); |
| |
| mPmInternal = LocalServices.getService(PackageManagerInternal.class); |
| final IntentFilter suspendPackagesFilter = new IntentFilter(); |
| suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); |
| suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); |
| context.registerReceiverAsUser(new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| final String[] affectedPackages = |
| intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); |
| final boolean suspended = |
| Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction()); |
| updateHiddenWhileSuspendedState(new ArraySet<>(Arrays.asList(affectedPackages)), |
| suspended); |
| } |
| }, UserHandle.ALL, suspendPackagesFilter, null, null); |
| |
| final ContentResolver resolver = context.getContentResolver(); |
| // Get persisted window scale setting |
| mWindowAnimationScaleSetting = Settings.Global.getFloat(resolver, |
| Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting); |
| mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver, |
| Settings.Global.TRANSITION_ANIMATION_SCALE, |
| context.getResources().getFloat( |
| R.dimen.config_appTransitionAnimationDurationScaleDefault)); |
| |
| setAnimatorDurationScale(Settings.Global.getFloat(resolver, |
| Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting)); |
| |
| mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver, |
| DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; |
| |
| IntentFilter filter = new IntentFilter(); |
| // Track changes to DevicePolicyManager state so we can enable/disable keyguard. |
| filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); |
| mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); |
| |
| mLatencyTracker = LatencyTracker.getInstance(context); |
| |
| mSettingsObserver = new SettingsObserver(); |
| |
| mHoldingScreenWakeLock = mPowerManager.newWakeLock( |
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM); |
| mHoldingScreenWakeLock.setReferenceCounted(false); |
| |
| mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory, |
| mPowerManagerInternal); |
| |
| mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( |
| com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); |
| |
| mTaskPositioningController = new TaskPositioningController( |
| this, mInputManager, mActivityTaskManager, mH.getLooper()); |
| mDragDropController = new DragDropController(this, mH.getLooper()); |
| |
| mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources()); |
| |
| mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL); |
| mConstants.start(new HandlerExecutor(mH)); |
| |
| LocalServices.addService(WindowManagerInternal.class, new LocalService()); |
| mEmbeddedWindowController = new EmbeddedWindowController(mGlobalLock); |
| setGlobalShadowSettings(); |
| } |
| |
| private void setGlobalShadowSettings() { |
| final TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); |
| float lightY = a.getDimension(R.styleable.Lighting_lightY, 0); |
| float lightZ = a.getDimension(R.styleable.Lighting_lightZ, 0); |
| float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0); |
| float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0); |
| float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0); |
| float[] ambientColor = {0.f, 0.f, 0.f, ambientShadowAlpha}; |
| float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha}; |
| SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ, |
| lightRadius); |
| setShadowRenderer(); |
| } |
| |
| /** |
| * Called after all entities (such as the {@link ActivityManagerService}) have been set up and |
| * associated with the {@link WindowManagerService}. |
| */ |
| public void onInitReady() { |
| initPolicy(); |
| |
| // Add ourself to the Watchdog monitors. |
| Watchdog.getInstance().addMonitor(this); |
| createWatermark(); |
| showEmulatorDisplayOverlayIfNeeded(); |
| } |
| |
| public InputManagerCallback getInputManagerCallback() { |
| return mInputManagerCallback; |
| } |
| |
| @Override |
| public boolean onTransact(int code, Parcel data, Parcel reply, int flags) |
| throws RemoteException { |
| try { |
| return super.onTransact(code, data, reply, flags); |
| } catch (RuntimeException e) { |
| // The window manager only throws security exceptions, so let's |
| // log all others. |
| if (!(e instanceof SecurityException)) { |
| ProtoLog.wtf(WM_ERROR, "Window Manager Crash %s", e); |
| } |
| throw e; |
| } |
| } |
| |
| static boolean excludeWindowTypeFromTapOutTask(int windowType) { |
| switch (windowType) { |
| case TYPE_STATUS_BAR: |
| case TYPE_NOTIFICATION_SHADE: |
| case TYPE_NAVIGATION_BAR: |
| case TYPE_INPUT_METHOD_DIALOG: |
| return true; |
| } |
| return false; |
| } |
| |
| public int addWindow(Session session, IWindow client, int seq, |
| LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, |
| Rect outContentInsets, Rect outStableInsets, |
| DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, |
| InsetsState outInsetsState) { |
| int[] appOp = new int[1]; |
| int res = mPolicy.checkAddPermission(attrs, appOp); |
| if (res != WindowManagerGlobal.ADD_OKAY) { |
| return res; |
| } |
| |
| WindowState parentWindow = null; |
| long origId; |
| final int callingUid = Binder.getCallingUid(); |
| final int type = attrs.type; |
| |
| synchronized (mGlobalLock) { |
| if (!mDisplayReady) { |
| throw new IllegalStateException("Display has not been initialialized"); |
| } |
| |
| final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token); |
| |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does " |
| + "not exist: %d. Aborting.", displayId); |
| return WindowManagerGlobal.ADD_INVALID_DISPLAY; |
| } |
| if (!displayContent.hasAccess(session.mUid)) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to add window to a display for which the application " |
| + "does not have access: %d. Aborting.", displayId); |
| return WindowManagerGlobal.ADD_INVALID_DISPLAY; |
| } |
| |
| if (mWindowMap.containsKey(client.asBinder())) { |
| ProtoLog.w(WM_ERROR, "Window %s is already added", client); |
| return WindowManagerGlobal.ADD_DUPLICATE_ADD; |
| } |
| |
| if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { |
| parentWindow = windowForClientLocked(null, attrs.token, false); |
| if (parentWindow == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; |
| } |
| if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW |
| && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { |
| ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; |
| } |
| } |
| |
| if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to add private presentation window to a non-private display. " |
| + "Aborting."); |
| return WindowManagerGlobal.ADD_PERMISSION_DENIED; |
| } |
| |
| ActivityRecord activity = null; |
| final boolean hasParent = parentWindow != null; |
| // Use existing parent window token for child windows since they go in the same token |
| // as there parent window so we can apply the same policy on them. |
| WindowToken token = displayContent.getWindowToken( |
| hasParent ? parentWindow.mAttrs.token : attrs.token); |
| // If this is a child window, we want to apply the same type checking rules as the |
| // parent window type. |
| final int rootType = hasParent ? parentWindow.mAttrs.type : type; |
| |
| boolean addToastWindowRequiresToken = false; |
| |
| if (token == null) { |
| if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type, |
| rootType, attrs.token, attrs.packageName)) { |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); |
| final boolean isRoundedCornerOverlay = |
| (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0; |
| token = new WindowToken(this, binder, type, false, displayContent, |
| session.mCanAddInternalSystemWindow, isRoundedCornerOverlay); |
| } else if (rootType >= FIRST_APPLICATION_WINDOW |
| && rootType <= LAST_APPLICATION_WINDOW) { |
| activity = token.asActivityRecord(); |
| if (activity == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token " |
| + ".%s Aborting.", token); |
| return WindowManagerGlobal.ADD_NOT_APP_TOKEN; |
| } else if (activity.getParent() == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token " |
| + ".%s Aborting.", token); |
| return WindowManagerGlobal.ADD_APP_EXITING; |
| } else if (type == TYPE_APPLICATION_STARTING && activity.startingWindow != null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to add starting window to token with already existing" |
| + " starting window"); |
| return WindowManagerGlobal.ADD_DUPLICATE_ADD; |
| } |
| } else if (rootType == TYPE_INPUT_METHOD) { |
| if (token.windowType != TYPE_INPUT_METHOD) { |
| ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (rootType == TYPE_VOICE_INTERACTION) { |
| if (token.windowType != TYPE_VOICE_INTERACTION) { |
| ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (rootType == TYPE_WALLPAPER) { |
| if (token.windowType != TYPE_WALLPAPER) { |
| ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (rootType == TYPE_DREAM) { |
| if (token.windowType != TYPE_DREAM) { |
| ProtoLog.w(WM_ERROR, "Attempted to add Dream window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { |
| if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to add Accessibility overlay window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (type == TYPE_TOAST) { |
| // Apps targeting SDK above N MR1 cannot arbitrary add toast windows. |
| addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName, |
| callingUid, parentWindow); |
| if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) { |
| ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (type == TYPE_QS_DIALOG) { |
| if (token.windowType != TYPE_QS_DIALOG) { |
| ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token " |
| + "%s. Aborting.", attrs.token); |
| return WindowManagerGlobal.ADD_BAD_APP_TOKEN; |
| } |
| } else if (token.asActivityRecord() != null) { |
| ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d", |
| rootType); |
| // It is not valid to use an app token with other system types; we will |
| // instead make a new token for it (as if null had been passed in for the token). |
| attrs.token = null; |
| token = new WindowToken(this, client.asBinder(), type, false, displayContent, |
| session.mCanAddInternalSystemWindow); |
| } |
| |
| final WindowState win = new WindowState(this, session, client, token, parentWindow, |
| appOp[0], seq, attrs, viewVisibility, session.mUid, |
| session.mCanAddInternalSystemWindow); |
| if (win.mDeathRecipient == null) { |
| // Client has apparently died, so there is no reason to |
| // continue. |
| ProtoLog.w(WM_ERROR, "Adding window client %s" |
| + " that is dead, aborting.", client.asBinder()); |
| return WindowManagerGlobal.ADD_APP_EXITING; |
| } |
| |
| if (win.getDisplayContent() == null) { |
| ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed."); |
| return WindowManagerGlobal.ADD_INVALID_DISPLAY; |
| } |
| |
| final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); |
| displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(), |
| Binder.getCallingUid()); |
| |
| res = displayPolicy.validateAddingWindowLw(attrs); |
| if (res != WindowManagerGlobal.ADD_OKAY) { |
| return res; |
| } |
| |
| final boolean openInputChannels = (outInputChannel != null |
| && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0); |
| if (openInputChannels) { |
| win.openInputChannel(outInputChannel); |
| } |
| |
| // If adding a toast requires a token for this app we always schedule hiding |
| // toast windows to make sure they don't stick around longer then necessary. |
| // We hide instead of remove such windows as apps aren't prepared to handle |
| // windows being removed under them. |
| // |
| // If the app is older it can add toasts without a token and hence overlay |
| // other apps. To be maximally compatible with these apps we will hide the |
| // window after the toast timeout only if the focused window is from another |
| // UID, otherwise we allow unlimited duration. When a UID looses focus we |
| // schedule hiding all of its toast windows. |
| if (type == TYPE_TOAST) { |
| if (!displayContent.canAddToastWindowForUid(callingUid)) { |
| ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time."); |
| return WindowManagerGlobal.ADD_DUPLICATE_ADD; |
| } |
| // Make sure this happens before we moved focus as one can make the |
| // toast focusable to force it not being hidden after the timeout. |
| // Focusable toasts are always timed out to prevent a focused app to |
| // show a focusable toasts while it has focus which will be kept on |
| // the screen after the activity goes away. |
| if (addToastWindowRequiresToken |
| || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0 |
| || displayContent.mCurrentFocus == null |
| || displayContent.mCurrentFocus.mOwnerUid != callingUid) { |
| mH.sendMessageDelayed( |
| mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win), |
| win.mAttrs.hideTimeoutMilliseconds); |
| } |
| } |
| |
| // From now on, no exceptions or errors allowed! |
| |
| res = WindowManagerGlobal.ADD_OKAY; |
| if (displayContent.mCurrentFocus == null) { |
| displayContent.mWinAddedSinceNullFocus.add(win); |
| } |
| |
| if (excludeWindowTypeFromTapOutTask(type)) { |
| displayContent.mTapExcludedWindows.add(win); |
| } |
| |
| origId = Binder.clearCallingIdentity(); |
| |
| win.attach(); |
| mWindowMap.put(client.asBinder(), win); |
| win.initAppOpsState(); |
| |
| final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(), |
| UserHandle.getUserId(win.getOwningUid())); |
| win.setHiddenWhileSuspended(suspended); |
| |
| final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); |
| win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); |
| |
| final ActivityRecord tokenActivity = token.asActivityRecord(); |
| if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) { |
| tokenActivity.startingWindow = win; |
| ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", |
| activity, win); |
| } |
| |
| boolean imMayMove = true; |
| |
| win.mToken.addWindow(win); |
| displayPolicy.addWindowLw(win, attrs); |
| if (type == TYPE_INPUT_METHOD) { |
| displayContent.setInputMethodWindowLocked(win); |
| imMayMove = false; |
| } else if (type == TYPE_INPUT_METHOD_DIALOG) { |
| displayContent.computeImeTarget(true /* updateImeTarget */); |
| imMayMove = false; |
| } else { |
| if (type == TYPE_WALLPAPER) { |
| displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); |
| displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; |
| } else if ((attrs.flags & FLAG_SHOW_WALLPAPER) != 0) { |
| displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; |
| } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { |
| // If there is currently a wallpaper being shown, and |
| // the base layer of the new window is below the current |
| // layer of the target window, then adjust the wallpaper. |
| // This is to avoid a new window being placed between the |
| // wallpaper and its target. |
| displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| } |
| |
| // If the window is being added to a stack that's currently adjusted for IME, |
| // make sure to apply the same adjust to this new window. |
| win.applyAdjustForImeIfNeeded(); |
| |
| if (type == TYPE_DOCK_DIVIDER) { |
| mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win); |
| } |
| |
| final WindowStateAnimator winAnimator = win.mWinAnimator; |
| winAnimator.mEnterAnimationPending = true; |
| winAnimator.mEnteringAnimation = true; |
| // Check if we need to prepare a transition for replacing window first. |
| if (activity != null && activity.isVisible() |
| && !prepareWindowReplacementTransition(activity)) { |
| // If not, check if need to set up a dummy transition during display freeze |
| // so that the unfreeze wait for the apps to draw. This might be needed if |
| // the app is relaunching. |
| prepareNoneTransitionForRelaunching(activity); |
| } |
| |
| final DisplayFrames displayFrames = displayContent.mDisplayFrames; |
| // TODO: Not sure if onDisplayInfoUpdated() call is needed. |
| final DisplayInfo displayInfo = displayContent.getDisplayInfo(); |
| displayFrames.onDisplayInfoUpdated(displayInfo, |
| displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation)); |
| final Rect taskBounds; |
| final boolean floatingStack; |
| if (activity != null && activity.getTask() != null) { |
| taskBounds = mTmpRect; |
| tokenActivity.getTask().getBounds(mTmpRect); |
| floatingStack = activity.getTask().isFloating(); |
| } else { |
| taskBounds = null; |
| floatingStack = false; |
| } |
| if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack, |
| outFrame, outContentInsets, outStableInsets, outDisplayCutout)) { |
| res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; |
| } |
| outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win), |
| win.mClient instanceof IWindow.Stub /* copySource */); |
| |
| if (mInTouchMode) { |
| res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; |
| } |
| if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) { |
| res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; |
| } |
| |
| displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); |
| |
| boolean focusChanged = false; |
| if (win.canReceiveKeys()) { |
| focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, |
| false /*updateInputWindows*/); |
| if (focusChanged) { |
| imMayMove = false; |
| } |
| } |
| |
| if (imMayMove) { |
| displayContent.computeImeTarget(true /* updateImeTarget */); |
| } |
| |
| // Don't do layout here, the window must call |
| // relayout to be displayed, so we'll do it there. |
| win.getParent().assignChildLayers(); |
| |
| if (focusChanged) { |
| displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, |
| false /*updateInputWindows*/); |
| } |
| displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); |
| |
| ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" |
| + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); |
| |
| |
| if (win.isVisibleOrAdding() && displayContent.updateOrientation()) { |
| displayContent.sendNewConfiguration(); |
| } |
| } |
| |
| Binder.restoreCallingIdentity(origId); |
| |
| return res; |
| } |
| |
| private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow, |
| int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) { |
| if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { |
| ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (rootType == TYPE_INPUT_METHOD) { |
| ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (rootType == TYPE_VOICE_INTERACTION) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to add voice interaction window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (rootType == TYPE_WALLPAPER) { |
| ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (rootType == TYPE_DREAM) { |
| ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (rootType == TYPE_QS_DIALOG) { |
| ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to add Accessibility overlay window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| if (type == TYPE_TOAST) { |
| // Apps targeting SDK above N MR1 cannot arbitrary add toast windows. |
| if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) { |
| ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token " |
| + "%s. Aborting.", tokenForLog); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Get existing {@link DisplayContent} or create a new one if the display is registered in |
| * DisplayManager. |
| * |
| * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent} |
| * that corresponds to a display just added to DisplayManager has not yet been created. This |
| * usually means that the call of this method was initiated from outside of Activity or Window |
| * Manager. In most cases the regular getter should be used. |
| * @param displayId The preferred display Id. |
| * @param token The window token associated with the window we are trying to get display for. |
| * if not null then the display of the window token will be returned. Set to null |
| * is there isn't an a token associated with the request. |
| * @see RootWindowContainer#getDisplayContent(int) |
| */ |
| private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) { |
| if (token != null) { |
| final WindowToken wToken = mRoot.getWindowToken(token); |
| if (wToken != null) { |
| return wToken.getDisplayContent(); |
| } |
| } |
| |
| return mRoot.getDisplayContentOrCreate(displayId); |
| } |
| |
| private boolean doesAddToastWindowRequireToken(String packageName, int callingUid, |
| WindowState attachedWindow) { |
| // Try using the target SDK of the root window |
| if (attachedWindow != null) { |
| return attachedWindow.mActivityRecord != null |
| && attachedWindow.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.O; |
| } else { |
| // Otherwise, look at the package |
| try { |
| ApplicationInfo appInfo = mContext.getPackageManager() |
| .getApplicationInfoAsUser(packageName, 0, |
| UserHandle.getUserId(callingUid)); |
| if (appInfo.uid != callingUid) { |
| throw new SecurityException("Package " + packageName + " not in UID " |
| + callingUid); |
| } |
| if (appInfo.targetSdkVersion >= Build.VERSION_CODES.O) { |
| return true; |
| } |
| } catch (PackageManager.NameNotFoundException e) { |
| /* ignore */ |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns true if we're done setting up any transitions. |
| */ |
| private boolean prepareWindowReplacementTransition(ActivityRecord activity) { |
| activity.clearAllDrawn(); |
| final WindowState replacedWindow = activity.getReplacingWindow(); |
| if (replacedWindow == null) { |
| // We expect to already receive a request to remove the old window. If it did not |
| // happen, let's just simply add a window. |
| return false; |
| } |
| // We use the visible frame, because we want the animation to morph the window from what |
| // was visible to the user to the final destination of the new window. |
| Rect frame = replacedWindow.getVisibleFrameLw(); |
| // We treat this as if this activity was opening, so we can trigger the app transition |
| // animation and piggy-back on existing transition animation infrastructure. |
| final DisplayContent dc = activity.getDisplayContent(); |
| dc.mOpeningApps.add(activity); |
| dc.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT, |
| 0 /* flags */, false /* forceOverride */); |
| dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, |
| frame.width(), frame.height()); |
| dc.executeAppTransition(); |
| return true; |
| } |
| |
| private void prepareNoneTransitionForRelaunching(ActivityRecord activity) { |
| // Set up a none-transition and add the app to opening apps, so that the display |
| // unfreeze wait for the apps to be drawn. |
| // Note that if the display unfroze already because app unfreeze timed out, |
| // we don't set up the transition anymore and just let it go. |
| final DisplayContent dc = activity.getDisplayContent(); |
| if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) { |
| dc.mOpeningApps.add(activity); |
| dc.prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */, |
| false /* forceOverride */); |
| dc.executeAppTransition(); |
| } |
| } |
| |
| boolean isSecureLocked(WindowState w) { |
| if ((w.mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { |
| return true; |
| } |
| if (DevicePolicyCache.getInstance().getScreenCaptureDisabled( |
| UserHandle.getUserId(w.mOwnerUid))) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Set whether screen capture is disabled for all windows of a specific user from |
| * the device policy cache. |
| */ |
| @Override |
| public void refreshScreenCaptureDisabled(int userId) { |
| int callingUid = Binder.getCallingUid(); |
| if (callingUid != SYSTEM_UID) { |
| throw new SecurityException("Only system can call refreshScreenCaptureDisabled."); |
| } |
| |
| synchronized (mGlobalLock) { |
| // Update secure surface for all windows belonging to this user. |
| mRoot.setSecureSurfaceState(userId, |
| DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId)); |
| } |
| } |
| |
| void removeWindow(Session session, IWindow client) { |
| synchronized (mGlobalLock) { |
| WindowState win = windowForClientLocked(session, client, false); |
| if (win != null) { |
| win.removeIfPossible(); |
| return; |
| } |
| |
| // Remove embedded window map if the token belongs to an embedded window |
| mEmbeddedWindowController.remove(client); |
| } |
| } |
| |
| /** |
| * Performs some centralized bookkeeping clean-up on the window that is being removed. |
| * NOTE: Should only be called from {@link WindowState#removeImmediately()} |
| * TODO: Maybe better handled with a method {@link WindowContainer#removeChild} if we can |
| * figure-out a good way to have all parents of a WindowState doing the same thing without |
| * forgetting to add the wiring when a new parent of WindowState is added. |
| */ |
| void postWindowRemoveCleanupLocked(WindowState win) { |
| ProtoLog.v(WM_DEBUG_ADD_REMOVE, "postWindowRemoveCleanupLocked: %s", win); |
| mWindowMap.remove(win.mClient.asBinder()); |
| |
| final DisplayContent dc = win.getDisplayContent(); |
| dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */); |
| |
| win.resetAppOpsState(); |
| |
| if (dc.mCurrentFocus == null) { |
| dc.mWinRemovedSinceNullFocus.add(win); |
| } |
| mEmbeddedWindowController.removeWindowsWithHost(win); |
| mPendingRemove.remove(win); |
| mResizingWindows.remove(win); |
| updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */); |
| mWindowsChanged = true; |
| ProtoLog.v(WM_DEBUG_WINDOW_MOVEMENT, "Final remove of window: %s", win); |
| |
| final DisplayContent displayContent = win.getDisplayContent(); |
| if (displayContent.mInputMethodWindow == win) { |
| displayContent.setInputMethodWindowLocked(null); |
| } |
| |
| final WindowToken token = win.mToken; |
| final ActivityRecord activity = win.mActivityRecord; |
| ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing %s from %s", win, token); |
| // Window will already be removed from token before this post clean-up method is called. |
| if (token.isEmpty()) { |
| if (!token.mPersistOnEmpty) { |
| token.removeImmediately(); |
| } else if (activity != null) { |
| // TODO: Should this be moved into ActivityRecord.removeWindow? Might go away after |
| // re-factor. |
| activity.firstWindowDrawn = false; |
| activity.clearAllDrawn(); |
| final ActivityStack stack = activity.getStack(); |
| if (stack != null) { |
| stack.mExitingActivities.remove(activity); |
| } |
| } |
| } |
| |
| if (activity != null) { |
| activity.postWindowRemoveStartingWindowCleanup(win); |
| } |
| |
| if (win.mAttrs.type == TYPE_WALLPAPER) { |
| dc.mWallpaperController.clearLastWallpaperTimeoutTime(); |
| dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; |
| } else if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { |
| dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| |
| if (dc != null && !mWindowPlacerLocked.isInLayout()) { |
| dc.assignWindowLayers(true /* setLayoutNeeded */); |
| mWindowPlacerLocked.performSurfacePlacement(); |
| if (win.mActivityRecord != null) { |
| win.mActivityRecord.updateReportedVisibilityLocked(); |
| } |
| } |
| |
| dc.getInputMonitor().updateInputWindowsLw(true /*force*/); |
| } |
| |
| private void updateHiddenWhileSuspendedState(ArraySet<String> packages, boolean suspended) { |
| synchronized (mGlobalLock) { |
| mRoot.updateHiddenWhileSuspendedState(packages, suspended); |
| } |
| } |
| |
| private void updateAppOpsState() { |
| synchronized (mGlobalLock) { |
| mRoot.updateAppOpsState(); |
| } |
| } |
| |
| static void logSurface(WindowState w, String msg, boolean withStackTrace) { |
| String str = " SURFACE " + msg + ": " + w; |
| if (withStackTrace) { |
| logWithStack(TAG, str); |
| } else { |
| Slog.i(TAG_WM, str); |
| } |
| } |
| |
| static void logWithStack(String tag, String s) { |
| RuntimeException e = null; |
| if (SHOW_STACK_CRAWLS) { |
| e = new RuntimeException(); |
| e.fillInStackTrace(); |
| } |
| Slog.i(tag, s, e); |
| } |
| |
| void setTransparentRegionWindow(Session session, IWindow client, Region region) { |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| WindowState w = windowForClientLocked(session, client, false); |
| ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s", |
| region, w); |
| |
| if ((w != null) && w.mHasSurface) { |
| w.mWinAnimator.setTransparentRegionHintLocked(region); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, |
| Rect visibleInsets, Region touchableRegion) { |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| WindowState w = windowForClientLocked(session, client, false); |
| if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w |
| + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets |
| + ", visibleInsets=" + w.mGivenVisibleInsets + " -> " + visibleInsets |
| + ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion |
| + ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets); |
| if (w != null) { |
| w.mGivenInsetsPending = false; |
| w.mGivenContentInsets.set(contentInsets); |
| w.mGivenVisibleInsets.set(visibleInsets); |
| w.mGivenTouchableRegion.set(touchableRegion); |
| w.mTouchableInsets = touchableInsets; |
| if (w.mGlobalScale != 1) { |
| w.mGivenContentInsets.scale(w.mGlobalScale); |
| w.mGivenVisibleInsets.scale(w.mGlobalScale); |
| w.mGivenTouchableRegion.scale(w.mGlobalScale); |
| } |
| w.setDisplayLayoutNeeded(); |
| mWindowPlacerLocked.performSurfacePlacement(); |
| |
| // We need to report touchable region changes to accessibility. |
| if (mAccessibilityController != null) { |
| mAccessibilityController.onSomeWindowResizedOrMovedLocked( |
| w.getDisplayContent().getDisplayId()); |
| } |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| public void getWindowDisplayFrame(Session session, IWindow client, |
| Rect outDisplayFrame) { |
| synchronized (mGlobalLock) { |
| WindowState win = windowForClientLocked(session, client, false); |
| if (win == null) { |
| outDisplayFrame.setEmpty(); |
| return; |
| } |
| outDisplayFrame.set(win.getDisplayFrameLw()); |
| if (win.inSizeCompatMode()) { |
| outDisplayFrame.scale(win.mInvGlobalScale); |
| } |
| } |
| } |
| |
| public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) { |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController != null) { |
| WindowState window = mWindowMap.get(token); |
| if (window != null) { |
| mAccessibilityController.onRectangleOnScreenRequestedLocked( |
| window.getDisplayId(), rectangle); |
| } |
| } |
| } |
| } |
| |
| public IWindowId getWindowId(IBinder token) { |
| synchronized (mGlobalLock) { |
| WindowState window = mWindowMap.get(token); |
| return window != null ? window.mWindowId : null; |
| } |
| } |
| |
| public void pokeDrawLock(Session session, IBinder token) { |
| synchronized (mGlobalLock) { |
| WindowState window = windowForClientLocked(session, token, false); |
| if (window != null) { |
| window.pokeDrawLockLw(mDrawLockTimeoutMillis); |
| } |
| } |
| } |
| |
| private boolean hasStatusBarPermission(int pid, int uid) { |
| return mContext.checkPermission(permission.STATUS_BAR, pid, uid) |
| == PackageManager.PERMISSION_GRANTED; |
| } |
| |
| public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs, |
| int requestedWidth, int requestedHeight, int viewVisibility, int flags, |
| long frameNumber, Rect outFrame, Rect outContentInsets, |
| Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame, |
| DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration, |
| SurfaceControl outSurfaceControl, InsetsState outInsetsState, |
| Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) { |
| int result = 0; |
| boolean configChanged; |
| final int pid = Binder.getCallingPid(); |
| final int uid = Binder.getCallingUid(); |
| long origId = Binder.clearCallingIdentity(); |
| synchronized (mGlobalLock) { |
| final WindowState win = windowForClientLocked(session, client, false); |
| if (win == null) { |
| return 0; |
| } |
| final DisplayContent displayContent = win.getDisplayContent(); |
| final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); |
| |
| WindowStateAnimator winAnimator = win.mWinAnimator; |
| if (viewVisibility != View.GONE) { |
| win.setRequestedSize(requestedWidth, requestedHeight); |
| } |
| |
| win.setFrameNumber(frameNumber); |
| |
| final DisplayContent dc = win.getDisplayContent(); |
| if (!dc.mWaitingForConfig) { |
| win.finishSeamlessRotation(false /* timeout */); |
| } |
| |
| int attrChanges = 0; |
| int flagChanges = 0; |
| int privateFlagChanges = 0; |
| if (attrs != null) { |
| displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); |
| // if they don't have the permission, mask out the status bar bits |
| if (seq == win.mSeq) { |
| int systemUiVisibility = attrs.systemUiVisibility |
| | attrs.subtreeSystemUiVisibility; |
| if ((systemUiVisibility & DISABLE_MASK) != 0) { |
| if (!hasStatusBarPermission(pid, uid)) { |
| systemUiVisibility &= ~DISABLE_MASK; |
| } |
| } |
| win.mSystemUiVisibility = systemUiVisibility; |
| } |
| if (win.mAttrs.type != attrs.type) { |
| throw new IllegalArgumentException( |
| "Window type can not be changed after the window is added."); |
| } |
| |
| // Odd choice but less odd than embedding in copyFrom() |
| if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) |
| != 0) { |
| attrs.x = win.mAttrs.x; |
| attrs.y = win.mAttrs.y; |
| attrs.width = win.mAttrs.width; |
| attrs.height = win.mAttrs.height; |
| } |
| |
| flagChanges = win.mAttrs.flags ^ attrs.flags; |
| privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags; |
| attrChanges = win.mAttrs.copyFrom(attrs); |
| if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED |
| | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) { |
| win.mLayoutNeeded = true; |
| } |
| if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0 |
| || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) { |
| win.mActivityRecord.checkKeyguardFlagsChanged(); |
| } |
| if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0) |
| && (mAccessibilityController != null)) { |
| // No move or resize, but the controller checks for title changes as well |
| mAccessibilityController.onSomeWindowResizedOrMovedLocked( |
| win.getDisplayContent().getDisplayId()); |
| } |
| |
| if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) { |
| updateNonSystemOverlayWindowsVisibilityIfNeeded( |
| win, win.mWinAnimator.getShown()); |
| } |
| if ((attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED)) != 0) { |
| winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags |
| & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0); |
| } |
| } |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility |
| + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); |
| winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0; |
| if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { |
| winAnimator.mAlpha = attrs.alpha; |
| } |
| win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight); |
| |
| if (win.mAttrs.surfaceInsets.left != 0 |
| || win.mAttrs.surfaceInsets.top != 0 |
| || win.mAttrs.surfaceInsets.right != 0 |
| || win.mAttrs.surfaceInsets.bottom != 0) { |
| winAnimator.setOpaqueLocked(false); |
| } |
| |
| final int oldVisibility = win.mViewVisibility; |
| |
| // If the window is becoming visible, visibleOrAdding may change which may in turn |
| // change the IME target. |
| final boolean becameVisible = |
| (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE) |
| && viewVisibility == View.VISIBLE; |
| boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0 |
| || becameVisible; |
| boolean focusMayChange = win.mViewVisibility != viewVisibility |
| || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0) |
| || (!win.mRelayoutCalled); |
| |
| boolean wallpaperMayMove = win.mViewVisibility != viewVisibility |
| && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; |
| wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0; |
| if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) { |
| winAnimator.mSurfaceController.setSecure(isSecureLocked(win)); |
| } |
| |
| win.mRelayoutCalled = true; |
| win.mInRelayout = true; |
| |
| win.mViewVisibility = viewVisibility; |
| ProtoLog.i(WM_DEBUG_SCREEN_ON, |
| "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility, |
| viewVisibility, new RuntimeException().fillInStackTrace()); |
| |
| |
| win.setDisplayLayoutNeeded(); |
| win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; |
| |
| // We should only relayout if the view is visible, it is a starting window, or the |
| // associated appToken is not hidden. |
| final boolean shouldRelayout = viewVisibility == View.VISIBLE && |
| (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING |
| || win.mActivityRecord.isClientVisible()); |
| |
| // If we are not currently running the exit animation, we need to see about starting |
| // one. |
| // We don't want to animate visibility of windows which are pending replacement. |
| // In the case of activity relaunch child windows could request visibility changes as |
| // they are detached from the main application window during the tear down process. |
| // If we satisfied these visibility changes though, we would cause a visual glitch |
| // hiding the window before it's replacement was available. So we just do nothing on |
| // our side. |
| // This must be called before the call to performSurfacePlacement. |
| if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) { |
| if (DEBUG_VISIBILITY) { |
| Slog.i(TAG_WM, |
| "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit); |
| } |
| result |= RELAYOUT_RES_SURFACE_CHANGED; |
| if (!win.mWillReplaceWindow) { |
| focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange); |
| } |
| } |
| |
| // We may be deferring layout passes at the moment, but since the client is interested |
| // in the new out values right now we need to force a layout. |
| mWindowPlacerLocked.performSurfacePlacement(true /* force */); |
| |
| if (shouldRelayout) { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); |
| |
| result = win.relayoutVisibleWindow(result, attrChanges); |
| |
| try { |
| result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl, |
| result, win, winAnimator); |
| } catch (Exception e) { |
| displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); |
| |
| ProtoLog.w(WM_ERROR, |
| "Exception thrown when creating surface for client %s (%s). %s", |
| client, win.mAttrs.getTitle(), e); |
| Binder.restoreCallingIdentity(origId); |
| return 0; |
| } |
| if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { |
| focusMayChange = true; |
| } |
| if (win.mAttrs.type == TYPE_INPUT_METHOD |
| && displayContent.mInputMethodWindow == null) { |
| displayContent.setInputMethodWindowLocked(win); |
| imMayMove = true; |
| } |
| win.adjustStartingWindowFlags(); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } else { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2"); |
| |
| winAnimator.mEnterAnimationPending = false; |
| winAnimator.mEnteringAnimation = false; |
| |
| if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) { |
| // We already told the client to go invisible, but the message may not be |
| // handled yet, or it might want to draw a last frame. If we already have a |
| // surface, let the client use that, but don't create new surface at this point. |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface"); |
| winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl); |
| winAnimator.mSurfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } else { |
| if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); |
| |
| try { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_" |
| + win.mAttrs.getTitle()); |
| outSurfaceControl.release(); |
| } finally { |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| } |
| |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| |
| if (focusMayChange) { |
| if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) { |
| imMayMove = false; |
| } |
| } |
| |
| // updateFocusedWindowLocked() already assigned layers so we only need to |
| // reassign them at this point if the IM window state gets shuffled |
| boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; |
| if (imMayMove) { |
| displayContent.computeImeTarget(true /* updateImeTarget */); |
| if (toBeDisplayed) { |
| // Little hack here -- we -should- be able to rely on the function to return |
| // true if the IME has moved and needs its layer recomputed. However, if the IME |
| // was hidden and isn't actually moved in the list, its layer may be out of data |
| // so we make sure to recompute it. |
| displayContent.assignWindowLayers(false /* setLayoutNeeded */); |
| } |
| } |
| |
| if (wallpaperMayMove) { |
| displayContent.pendingLayoutChanges |= |
| WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| |
| if (win.mActivityRecord != null) { |
| displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord); |
| } |
| |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation"); |
| configChanged = displayContent.updateOrientation(); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| |
| if (toBeDisplayed && win.mIsWallpaper) { |
| DisplayInfo displayInfo = displayContent.getDisplayInfo(); |
| displayContent.mWallpaperController.updateWallpaperOffset( |
| win, displayInfo.logicalWidth, displayInfo.logicalHeight, false); |
| } |
| if (win.mActivityRecord != null) { |
| win.mActivityRecord.updateReportedVisibilityLocked(); |
| } |
| if (winAnimator.mReportSurfaceResized) { |
| winAnimator.mReportSurfaceResized = false; |
| result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED; |
| } |
| if (displayPolicy.areSystemBarsForcedShownLw(win)) { |
| result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; |
| } |
| if (!win.isGoneForLayoutLw()) { |
| win.mResizedWhileGone = false; |
| } |
| |
| // We must always send the latest {@link MergedConfiguration}, regardless of whether we |
| // have already reported it. The client might not have processed the previous value yet |
| // and needs process it before handling the corresponding window frame. the variable |
| // {@code mergedConfiguration} is an out parameter that will be passed back to the |
| // client over IPC and checked there. |
| // Note: in the cases where the window is tied to an activity, we should not send a |
| // configuration update when the window has requested to be hidden. Doing so can lead |
| // to the client erroneously accepting a configuration that would have otherwise caused |
| // an activity restart. We instead hand back the last reported |
| // {@link MergedConfiguration}. |
| if (shouldRelayout) { |
| win.getMergedConfiguration(mergedConfiguration); |
| } else { |
| win.getLastReportedMergedConfiguration(mergedConfiguration); |
| } |
| |
| win.setLastReportedMergedConfiguration(mergedConfiguration); |
| |
| // Update the last inset values here because the values are sent back to the client. |
| // The last inset values represent the last client state |
| win.updateLastInsetValues(); |
| |
| win.getCompatFrame(outFrame); |
| win.getInsetsForRelayout(outContentInsets, outVisibleInsets, |
| outStableInsets); |
| outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); |
| outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); |
| outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win), |
| win.mClient instanceof IWindow.Stub /* copySource */); |
| if (DEBUG) { |
| Slog.v(TAG_WM, "Relayout given client " + client.asBinder() |
| + ", requestedWidth=" + requestedWidth |
| + ", requestedHeight=" + requestedHeight |
| + ", viewVisibility=" + viewVisibility |
| + "\nRelayout returning frame=" + outFrame |
| + ", surface=" + outSurfaceControl); |
| } |
| |
| ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b", |
| win, focusMayChange); |
| |
| result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0; |
| |
| if (DEBUG_LAYOUT) { |
| Slog.v(TAG_WM, |
| "Relayout complete " + win + ": outFrame=" + outFrame.toShortString()); |
| } |
| win.mInRelayout = false; |
| |
| if (configChanged) { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, |
| "relayoutWindow: postNewConfigurationToHandler"); |
| displayContent.sendNewConfiguration(); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| if (winAnimator.mSurfaceController != null) { |
| outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(), |
| winAnimator.mSurfaceController.getHeight()); |
| } |
| } |
| |
| Binder.restoreCallingIdentity(origId); |
| return result; |
| } |
| |
| private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator, |
| boolean focusMayChange) { |
| // Try starting an animation; if there isn't one, we |
| // can destroy the surface right away. |
| int transit = WindowManagerPolicy.TRANSIT_EXIT; |
| if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { |
| transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; |
| } |
| if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) { |
| focusMayChange = true; |
| win.mAnimatingExit = true; |
| } else if (win.isAnimating(TRANSITION | PARENTS)) { |
| // Currently in a hide animation... turn this into |
| // an exit. |
| win.mAnimatingExit = true; |
| } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) { |
| // If the wallpaper is currently behind this |
| // window, we need to change both of them inside |
| // of a transaction to avoid artifacts. |
| win.mAnimatingExit = true; |
| } else { |
| final DisplayContent displayContent = win.getDisplayContent(); |
| if (displayContent.mInputMethodWindow == win) { |
| displayContent.setInputMethodWindowLocked(null); |
| } |
| boolean stopped = win.mActivityRecord != null ? win.mActivityRecord.mAppStopped : true; |
| // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces |
| // will later actually destroy the surface if we do not do so here. Normally we leave |
| // this to the exit animation. |
| win.mDestroying = true; |
| win.destroySurface(false, stopped); |
| } |
| if (mAccessibilityController != null) { |
| mAccessibilityController.onWindowTransitionLocked(win, transit); |
| } |
| |
| // When we start the exit animation we take the Surface from the client |
| // so it will stop perturbing it. We need to likewise takeaway the SurfaceFlinger |
| // side child surfaces, so they will remain preserved in their current state |
| // (rather than be cleaned up immediately by the app code). |
| SurfaceControl.openTransaction(); |
| winAnimator.detachChildren(); |
| SurfaceControl.closeTransaction(); |
| |
| return focusMayChange; |
| } |
| |
| private int createSurfaceControl(SurfaceControl outSurfaceControl, |
| SurfaceControl outBLASTSurfaceControl, int result, |
| WindowState win, WindowStateAnimator winAnimator) { |
| if (!win.mHasSurface) { |
| result |= RELAYOUT_RES_SURFACE_CHANGED; |
| } |
| |
| WindowSurfaceController surfaceController; |
| try { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl"); |
| surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid); |
| } finally { |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| if (surfaceController != null) { |
| surfaceController.getSurfaceControl(outSurfaceControl); |
| surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl); |
| ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl); |
| |
| } else { |
| // For some reason there isn't a surface. Clear the |
| // caller's object so they see the same state. |
| ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win); |
| outSurfaceControl.release(); |
| } |
| |
| return result; |
| } |
| |
| public boolean outOfMemoryWindow(Session session, IWindow client) { |
| final long origId = Binder.clearCallingIdentity(); |
| |
| try { |
| synchronized (mGlobalLock) { |
| WindowState win = windowForClientLocked(session, client, false); |
| if (win == null) { |
| return false; |
| } |
| return mRoot.reclaimSomeSurfaceMemory(win.mWinAnimator, "from-client", false); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| void finishDrawingWindow(Session session, IWindow client, |
| @Nullable SurfaceControl.Transaction postDrawTransaction) { |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| WindowState win = windowForClientLocked(session, client, false); |
| ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s", |
| win, (win != null ? win.mWinAnimator.drawStateToString() : "null")); |
| if (win != null && win.mWinAnimator.finishDrawingLocked(postDrawTransaction)) { |
| if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { |
| win.getDisplayContent().pendingLayoutChanges |= |
| WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| win.setDisplayLayoutNeeded(); |
| mWindowPlacerLocked.requestTraversal(); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| boolean checkCallingPermission(String permission, String func) { |
| // Quick check: if the calling permission is me, it's all okay. |
| if (Binder.getCallingPid() == myPid()) { |
| return true; |
| } |
| |
| if (mContext.checkCallingPermission(permission) |
| == PackageManager.PERMISSION_GRANTED) { |
| return true; |
| } |
| ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s", |
| func, Binder.getCallingPid(), Binder.getCallingUid(), permission); |
| return false; |
| } |
| |
| @Override |
| public void addWindowToken(IBinder binder, int type, int displayId) { |
| addWindowTokenWithOptions(binder, type, displayId, null /* options */, |
| null /* packageName */); |
| } |
| |
| public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, |
| String packageName) { |
| final boolean callerCanManageAppTokens = |
| checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()"); |
| if (!callerCanManageAppTokens) { |
| // TODO(window-context): refactor checkAddPermission to not take attrs. |
| LayoutParams attrs = new LayoutParams(type); |
| attrs.packageName = packageName; |
| final int res = mPolicy.checkAddPermission(attrs, new int[1]); |
| if (res != ADD_OKAY) { |
| return res; |
| } |
| } |
| |
| synchronized (mGlobalLock) { |
| if (!callerCanManageAppTokens) { |
| if (!unprivilegedAppCanCreateTokenWith(null, Binder.getCallingUid(), type, type, |
| null, packageName) || packageName == null) { |
| throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); |
| } |
| } |
| |
| final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); |
| if (dc == null) { |
| ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s" |
| + " for non-exiting displayId=%d", binder, displayId); |
| return WindowManagerGlobal.ADD_INVALID_DISPLAY; |
| } |
| |
| WindowToken token = dc.getWindowToken(binder); |
| if (token != null) { |
| ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s" |
| + " for already created window token: %s" |
| + " displayId=%d", binder, token, displayId); |
| return WindowManagerGlobal.ADD_DUPLICATE_ADD; |
| } |
| // TODO(window-container): Clean up dead tokens |
| if (type == TYPE_WALLPAPER) { |
| new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens); |
| } else { |
| new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens); |
| } |
| } |
| return WindowManagerGlobal.ADD_OKAY; |
| } |
| |
| @Override |
| public boolean isWindowToken(IBinder binder) { |
| synchronized (mGlobalLock) { |
| final WindowToken windowToken = mRoot.getWindowToken(binder); |
| if (windowToken == null) { |
| return false; |
| } |
| // We don't allow activity tokens in WindowContext. TODO(window-context): rename method |
| return windowToken.asActivityRecord() == null; |
| } |
| } |
| |
| @Override |
| public void removeWindowToken(IBinder binder, int displayId) { |
| if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { |
| throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); |
| } |
| |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" |
| + " for non-exiting displayId=%d", binder, displayId); |
| return; |
| } |
| |
| final WindowToken token = dc.removeWindowToken(binder); |
| if (token == null) { |
| ProtoLog.w(WM_ERROR, |
| "removeWindowToken: Attempted to remove non-existing token: %s", |
| binder); |
| return; |
| } |
| |
| dc.getInputMonitor().updateInputWindowsLw(true /*force*/); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| void setNewDisplayOverrideConfiguration(Configuration overrideConfig, |
| @NonNull DisplayContent dc) { |
| if (dc.mWaitingForConfig) { |
| dc.mWaitingForConfig = false; |
| mLastFinishedFreezeSource = "new-config"; |
| } |
| |
| mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, dc); |
| } |
| |
| // TODO(multi-display): remove when no default display use case. |
| // (i.e. KeyguardController / RecentsAnimation) |
| @Override |
| public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) { |
| if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) { |
| throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); |
| } |
| getDefaultDisplayContentLocked().prepareAppTransition(transit, |
| alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */); |
| } |
| |
| @Override |
| public void overridePendingAppTransitionMultiThumbFuture( |
| IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, |
| boolean scaleUp, int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| Slog.w(TAG, "Attempted to call overridePendingAppTransitionMultiThumbFuture" |
| + " for the display " + displayId + " that does not exist."); |
| return; |
| } |
| displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, |
| callback, scaleUp); |
| } |
| } |
| |
| @Override |
| public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter, |
| int displayId) { |
| if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, |
| "overridePendingAppTransitionRemote()")) { |
| throw new SecurityException( |
| "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission"); |
| } |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| Slog.w(TAG, "Attempted to call overridePendingAppTransitionRemote" |
| + " for the display " + displayId + " that does not exist."); |
| return; |
| } |
| displayContent.mAppTransition.overridePendingAppTransitionRemote( |
| remoteAnimationAdapter); |
| } |
| } |
| |
| @Override |
| public void endProlongedAnimations() { |
| // TODO: Remove once clients are updated. |
| } |
| |
| // TODO(multi-display): remove when no default display use case. |
| // (i.e. KeyguardController / RecentsAnimation) |
| @Override |
| public void executeAppTransition() { |
| if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) { |
| throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); |
| } |
| getDefaultDisplayContentLocked().executeAppTransition(); |
| } |
| |
| void initializeRecentsAnimation(int targetActivityType, |
| IRecentsAnimationRunner recentsAnimationRunner, |
| RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId, |
| SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) { |
| mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner, |
| callbacks, displayId); |
| mRoot.getDisplayContent(displayId).mAppTransition.updateBooster(); |
| mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity); |
| } |
| |
| @VisibleForTesting |
| void setRecentsAnimationController(RecentsAnimationController controller) { |
| mRecentsAnimationController = controller; |
| } |
| |
| RecentsAnimationController getRecentsAnimationController() { |
| return mRecentsAnimationController; |
| } |
| |
| /** |
| * @return Whether the next recents animation can continue to start. Called from |
| * {@link RecentsAnimation#startRecentsActivity}. |
| */ |
| boolean canStartRecentsAnimation() { |
| // TODO(multi-display): currently only default display support recent activity |
| if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) { |
| return false; |
| } |
| return true; |
| } |
| |
| void cancelRecentsAnimation( |
| @RecentsAnimationController.ReorderMode int reorderMode, String reason) { |
| if (mRecentsAnimationController != null) { |
| // This call will call through to cleanupAnimation() below after the animation is |
| // canceled |
| mRecentsAnimationController.cancelAnimation(reorderMode, reason); |
| } |
| } |
| |
| void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { |
| if (mRecentsAnimationController != null) { |
| final RecentsAnimationController controller = mRecentsAnimationController; |
| mRecentsAnimationController = null; |
| controller.cleanupAnimation(reorderMode); |
| // TODO(mult-display): currently only default display support recents animation. |
| getDefaultDisplayContentLocked().mAppTransition.updateBooster(); |
| } |
| } |
| |
| void setWindowOpaqueLocked(IBinder token, boolean isOpaque) { |
| final ActivityRecord wtoken = mRoot.getActivityRecord(token); |
| if (wtoken != null) { |
| wtoken.setMainWindowOpaque(isOpaque); |
| } |
| } |
| |
| void setDockedStackCreateStateLocked(int mode, Rect bounds) { |
| mDockedStackCreateMode = mode; |
| mDockedStackCreateBounds = bounds; |
| } |
| |
| void checkSplitScreenMinimizedChanged(boolean animate) { |
| final DisplayContent displayContent = getDefaultDisplayContentLocked(); |
| displayContent.getDockedDividerController().checkMinimizeChanged(animate); |
| } |
| |
| boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) { |
| return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio( |
| aspectRatio); |
| } |
| |
| @Override |
| public void getStackBounds(int windowingMode, int activityType, Rect bounds) { |
| synchronized (mGlobalLock) { |
| final ActivityStack stack = mRoot.getStack(windowingMode, activityType); |
| if (stack != null) { |
| stack.getBounds(bounds); |
| return; |
| } |
| bounds.setEmpty(); |
| } |
| } |
| |
| /** |
| * Notifies window manager that {@link DisplayPolicy#isShowingDreamLw} has changed. |
| */ |
| public void notifyShowingDreamChanged() { |
| // TODO(multi-display): support show dream in multi-display. |
| notifyKeyguardFlagsChanged(null /* callback */, DEFAULT_DISPLAY); |
| } |
| |
| @Override |
| public WindowManagerPolicy.WindowState getInputMethodWindowLw() { |
| return mRoot.getCurrentInputMethodWindow(); |
| } |
| |
| @Override |
| public void notifyKeyguardTrustedChanged() { |
| mAtmInternal.notifyKeyguardTrustedChanged(); |
| } |
| |
| @Override |
| public void screenTurningOff(ScreenOffListener listener) { |
| mTaskSnapshotController.screenTurningOff(listener); |
| } |
| |
| @Override |
| public void triggerAnimationFailsafe() { |
| mH.sendEmptyMessage(H.ANIMATION_FAILSAFE); |
| } |
| |
| @Override |
| public void onKeyguardShowingAndNotOccludedChanged() { |
| mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); |
| } |
| |
| @Override |
| public void onPowerKeyDown(boolean isScreenOn) { |
| final PooledConsumer c = PooledLambda.obtainConsumer( |
| DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn); |
| mRoot.forAllDisplayPolicies(c); |
| c.recycle(); |
| } |
| |
| @Override |
| public void onUserSwitched() { |
| mSettingsObserver.updateSystemUiSettings(); |
| synchronized (mGlobalLock) { |
| // force a re-application of focused window sysui visibility on each display. |
| mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw); |
| } |
| } |
| |
| @Override |
| public void moveDisplayToTop(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null && mRoot.getTopChild() != displayContent) { |
| displayContent.positionDisplayAt(WindowContainer.POSITION_TOP, |
| true /* includingParents */); |
| } |
| } |
| syncInputTransactions(); |
| } |
| |
| /** |
| * Notifies activity manager that some Keyguard flags have changed and that it needs to |
| * reevaluate the visibilities of the activities. |
| * @param callback Runnable to be called when activity manager is done reevaluating visibilities |
| */ |
| void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId) { |
| mAtmInternal.notifyKeyguardFlagsChanged(callback, displayId); |
| } |
| |
| public void setKeyguardGoingAway(boolean keyguardGoingAway) { |
| synchronized (mGlobalLock) { |
| mKeyguardGoingAway = keyguardGoingAway; |
| } |
| } |
| |
| public void setKeyguardOrAodShowingOnDefaultDisplay(boolean showing) { |
| synchronized (mGlobalLock) { |
| mKeyguardOrAodShowingOnDefaultDisplay = showing; |
| } |
| } |
| |
| // ------------------------------------------------------------- |
| // Misc IWindowSession methods |
| // ------------------------------------------------------------- |
| |
| @Override |
| public void startFreezingScreen(int exitAnim, int enterAnim) { |
| if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, |
| "startFreezingScreen()")) { |
| throw new SecurityException("Requires FREEZE_SCREEN permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| if (!mClientFreezingScreen) { |
| mClientFreezingScreen = true; |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| startFreezingDisplayLocked(exitAnim, enterAnim); |
| mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); |
| mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void stopFreezingScreen() { |
| if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, |
| "stopFreezingScreen()")) { |
| throw new SecurityException("Requires FREEZE_SCREEN permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| if (mClientFreezingScreen) { |
| mClientFreezingScreen = false; |
| mLastFinishedFreezeSource = "client"; |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| stopFreezingDisplayLocked(); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void disableKeyguard(IBinder token, String tag, int userId) { |
| userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), |
| userId, false /* allowAll */, ALLOW_FULL_ONLY, "disableKeyguard", null); |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires DISABLE_KEYGUARD permission"); |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long origIdentity = Binder.clearCallingIdentity(); |
| try { |
| mKeyguardDisableHandler.disableKeyguard(token, tag, callingUid, userId); |
| } finally { |
| Binder.restoreCallingIdentity(origIdentity); |
| } |
| } |
| |
| @Override |
| public void reenableKeyguard(IBinder token, int userId) { |
| userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), |
| userId, false /* allowAll */, ALLOW_FULL_ONLY, "reenableKeyguard", null); |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires DISABLE_KEYGUARD permission"); |
| } |
| Objects.requireNonNull(token, "token is null"); |
| final int callingUid = Binder.getCallingUid(); |
| final long origIdentity = Binder.clearCallingIdentity(); |
| try { |
| mKeyguardDisableHandler.reenableKeyguard(token, callingUid, userId); |
| } finally { |
| Binder.restoreCallingIdentity(origIdentity); |
| } |
| } |
| |
| /** |
| * @see android.app.KeyguardManager#exitKeyguardSecurely |
| */ |
| @Override |
| public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) { |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires DISABLE_KEYGUARD permission"); |
| } |
| |
| if (callback == null) { |
| throw new IllegalArgumentException("callback == null"); |
| } |
| |
| mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() { |
| @Override |
| public void onKeyguardExitResult(boolean success) { |
| try { |
| callback.onKeyguardExitResult(success); |
| } catch (RemoteException e) { |
| // Client has died, we don't care. |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public boolean isKeyguardLocked() { |
| return mPolicy.isKeyguardLocked(); |
| } |
| |
| public boolean isKeyguardShowingAndNotOccluded() { |
| return mPolicy.isKeyguardShowingAndNotOccluded(); |
| } |
| |
| @Override |
| public boolean isKeyguardSecure(int userId) { |
| if (userId != UserHandle.getCallingUserId() |
| && !checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS, |
| "isKeyguardSecure")) { |
| throw new SecurityException("Requires INTERACT_ACROSS_USERS permission"); |
| } |
| |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| return mPolicy.isKeyguardSecure(userId); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) { |
| if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) { |
| throw new SecurityException("Requires CONTROL_KEYGUARD permission"); |
| } |
| synchronized (mGlobalLock) { |
| mPolicy.dismissKeyguardLw(callback, message); |
| } |
| } |
| |
| @Override |
| public void setSwitchingUser(boolean switching) { |
| if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, |
| "setSwitchingUser()")) { |
| throw new SecurityException("Requires INTERACT_ACROSS_USERS_FULL permission"); |
| } |
| mPolicy.setSwitchingUser(switching); |
| synchronized (mGlobalLock) { |
| mSwitchingUser = switching; |
| } |
| } |
| |
| @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW) |
| @Override |
| public void showGlobalActions() { |
| if (!checkCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, |
| "showGlobalActions()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| mPolicy.showGlobalActions(); |
| } |
| |
| @Override |
| public void closeSystemDialogs(String reason) { |
| synchronized (mGlobalLock) { |
| mRoot.closeSystemDialogs(reason); |
| } |
| } |
| |
| static float fixScale(float scale) { |
| if (scale < 0) scale = 0; |
| else if (scale > 20) scale = 20; |
| return Math.abs(scale); |
| } |
| |
| @Override |
| public void setAnimationScale(int which, float scale) { |
| if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, |
| "setAnimationScale()")) { |
| throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); |
| } |
| |
| scale = fixScale(scale); |
| switch (which) { |
| case 0: mWindowAnimationScaleSetting = scale; break; |
| case 1: mTransitionAnimationScaleSetting = scale; break; |
| case 2: mAnimatorDurationScaleSetting = scale; break; |
| } |
| |
| // Persist setting |
| mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); |
| } |
| |
| @Override |
| public void setAnimationScales(float[] scales) { |
| if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, |
| "setAnimationScale()")) { |
| throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); |
| } |
| |
| if (scales != null) { |
| if (scales.length >= 1) { |
| mWindowAnimationScaleSetting = fixScale(scales[0]); |
| } |
| if (scales.length >= 2) { |
| mTransitionAnimationScaleSetting = fixScale(scales[1]); |
| } |
| if (scales.length >= 3) { |
| mAnimatorDurationScaleSetting = fixScale(scales[2]); |
| dispatchNewAnimatorScaleLocked(null); |
| } |
| } |
| |
| // Persist setting |
| mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); |
| } |
| |
| private void setAnimatorDurationScale(float scale) { |
| mAnimatorDurationScaleSetting = scale; |
| ValueAnimator.setDurationScale(scale); |
| } |
| |
| public float getWindowAnimationScaleLocked() { |
| return mAnimationsDisabled ? 0 : mWindowAnimationScaleSetting; |
| } |
| |
| public float getTransitionAnimationScaleLocked() { |
| return mAnimationsDisabled ? 0 : mTransitionAnimationScaleSetting; |
| } |
| |
| @Override |
| public float getAnimationScale(int which) { |
| switch (which) { |
| case 0: return mWindowAnimationScaleSetting; |
| case 1: return mTransitionAnimationScaleSetting; |
| case 2: return mAnimatorDurationScaleSetting; |
| } |
| return 0; |
| } |
| |
| @Override |
| public float[] getAnimationScales() { |
| return new float[] { mWindowAnimationScaleSetting, mTransitionAnimationScaleSetting, |
| mAnimatorDurationScaleSetting }; |
| } |
| |
| @Override |
| public float getCurrentAnimatorScale() { |
| synchronized (mGlobalLock) { |
| return mAnimationsDisabled ? 0 : mAnimatorDurationScaleSetting; |
| } |
| } |
| |
| void dispatchNewAnimatorScaleLocked(Session session) { |
| mH.obtainMessage(H.NEW_ANIMATOR_SCALE, session).sendToTarget(); |
| } |
| |
| @Override |
| public void registerPointerEventListener(PointerEventListener listener, int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.registerPointerEventListener(listener); |
| } |
| } |
| } |
| |
| @Override |
| public void unregisterPointerEventListener(PointerEventListener listener, int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.unregisterPointerEventListener(listener); |
| } |
| } |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public int getLidState() { |
| int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, |
| InputManagerService.SW_LID); |
| if (sw > 0) { |
| // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL. |
| return LID_CLOSED; |
| } else if (sw == 0) { |
| // Switch state: AKEY_STATE_UP. |
| return LID_OPEN; |
| } else { |
| // Switch state: AKEY_STATE_UNKNOWN. |
| return LID_ABSENT; |
| } |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public void lockDeviceNow() { |
| lockNow(null); |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public int getCameraLensCoverState() { |
| int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, |
| InputManagerService.SW_CAMERA_LENS_COVER); |
| if (sw > 0) { |
| // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL. |
| return CAMERA_LENS_COVERED; |
| } else if (sw == 0) { |
| // Switch state: AKEY_STATE_UP. |
| return CAMERA_LENS_UNCOVERED; |
| } else { |
| // Switch state: AKEY_STATE_UNKNOWN. |
| return CAMERA_LENS_COVER_ABSENT; |
| } |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public void switchKeyboardLayout(int deviceId, int direction) { |
| mInputManager.switchKeyboardLayout(deviceId, direction); |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public void shutdown(boolean confirm) { |
| // Pass in the UI context, since ShutdownThread requires it (to show UI). |
| ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(), |
| PowerManager.SHUTDOWN_USER_REQUESTED, confirm); |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public void reboot(boolean confirm) { |
| // Pass in the UI context, since ShutdownThread requires it (to show UI). |
| ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(), |
| PowerManager.SHUTDOWN_USER_REQUESTED, confirm); |
| } |
| |
| // Called by window manager policy. Not exposed externally. |
| @Override |
| public void rebootSafeMode(boolean confirm) { |
| // Pass in the UI context, since ShutdownThread requires it (to show UI). |
| ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(), |
| confirm); |
| } |
| |
| public void setCurrentProfileIds(final int[] currentProfileIds) { |
| synchronized (mGlobalLock) { |
| mCurrentProfileIds = currentProfileIds; |
| } |
| } |
| |
| public void setCurrentUser(final int newUserId, final int[] currentProfileIds) { |
| synchronized (mGlobalLock) { |
| mCurrentUserId = newUserId; |
| mCurrentProfileIds = currentProfileIds; |
| mPolicy.setCurrentUserLw(newUserId); |
| mKeyguardDisableHandler.setCurrentUser(newUserId); |
| |
| // Hide windows that should not be seen by the new user. |
| mRoot.switchUser(newUserId); |
| mWindowPlacerLocked.performSurfacePlacement(); |
| |
| // Notify whether the docked stack exists for the current user |
| final DisplayContent displayContent = getDefaultDisplayContentLocked(); |
| final ActivityStack stack = |
| displayContent.getRootSplitScreenPrimaryTask(); |
| displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged( |
| stack != null && stack.hasTaskForUser(newUserId)); |
| |
| mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId)); |
| |
| // If the display is already prepared, update the density. |
| // Otherwise, we'll update it when it's prepared. |
| if (mDisplayReady) { |
| final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId); |
| final int targetDensity = forcedDensity != 0 ? forcedDensity |
| : displayContent.mInitialDisplayDensity; |
| displayContent.setForcedDensity(targetDensity, UserHandle.USER_CURRENT); |
| } |
| } |
| } |
| |
| /* Called by WindowState */ |
| boolean isCurrentProfile(int userId) { |
| if (userId == mCurrentUserId) return true; |
| for (int i = 0; i < mCurrentProfileIds.length; i++) { |
| if (mCurrentProfileIds[i] == userId) return true; |
| } |
| return false; |
| } |
| |
| public void enableScreenAfterBoot() { |
| synchronized (mGlobalLock) { |
| ProtoLog.i(WM_DEBUG_BOOT, "enableScreenAfterBoot: mDisplayEnabled=%b " |
| + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. " |
| + "%s", |
| mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, |
| new RuntimeException("here").fillInStackTrace()); |
| if (mSystemBooted) { |
| return; |
| } |
| mSystemBooted = true; |
| hideBootMessagesLocked(); |
| // If the screen still doesn't come up after 30 seconds, give |
| // up and turn it on. |
| mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000); |
| } |
| |
| mPolicy.systemBooted(); |
| |
| performEnableScreen(); |
| } |
| |
| @Override |
| public void enableScreenIfNeeded() { |
| synchronized (mGlobalLock) { |
| enableScreenIfNeededLocked(); |
| } |
| } |
| |
| void enableScreenIfNeededLocked() { |
| ProtoLog.i(WM_DEBUG_BOOT, "enableScreenIfNeededLocked: mDisplayEnabled=%b " |
| + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. " |
| + "%s", |
| mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, |
| new RuntimeException("here").fillInStackTrace()); |
| if (mDisplayEnabled) { |
| return; |
| } |
| if (!mSystemBooted && !mShowingBootMessages) { |
| return; |
| } |
| mH.sendEmptyMessage(H.ENABLE_SCREEN); |
| } |
| |
| public void performBootTimeout() { |
| synchronized (mGlobalLock) { |
| if (mDisplayEnabled) { |
| return; |
| } |
| ProtoLog.w(WM_ERROR, "***** BOOT TIMEOUT: forcing display enabled"); |
| mForceDisplayEnabled = true; |
| } |
| performEnableScreen(); |
| } |
| |
| /** |
| * Called when System UI has been started. |
| */ |
| public void onSystemUiStarted() { |
| mPolicy.onSystemUiStarted(); |
| } |
| |
| private void performEnableScreen() { |
| synchronized (mGlobalLock) { |
| ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b" |
| + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b" |
| + " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled, |
| mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, mOnlyCore, |
| new RuntimeException("here").fillInStackTrace()); |
| if (mDisplayEnabled) { |
| return; |
| } |
| if (!mSystemBooted && !mShowingBootMessages) { |
| return; |
| } |
| |
| if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) { |
| return; |
| } |
| |
| // Don't enable the screen until all existing windows have been drawn. |
| if (!mForceDisplayEnabled) { |
| for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { |
| if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) { |
| return; |
| } |
| } |
| } |
| |
| if (!mBootAnimationStopped) { |
| Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0); |
| // stop boot animation |
| // formerly we would just kill the process, but we now ask it to exit so it |
| // can choose where to stop the animation. |
| SystemProperties.set("service.bootanim.exit", "1"); |
| mBootAnimationStopped = true; |
| } |
| |
| if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) { |
| ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete"); |
| return; |
| } |
| |
| try { |
| IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); |
| if (surfaceFlinger != null) { |
| ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!"); |
| Parcel data = Parcel.obtain(); |
| data.writeInterfaceToken("android.ui.ISurfaceComposer"); |
| surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED |
| data, null, 0); |
| data.recycle(); |
| } |
| } catch (RemoteException ex) { |
| ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!"); |
| } |
| |
| EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis()); |
| Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0); |
| mDisplayEnabled = true; |
| ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!"); |
| |
| // Enable input dispatch. |
| mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled); |
| } |
| |
| try { |
| mActivityManager.bootAnimationComplete(); |
| } catch (RemoteException e) { |
| } |
| |
| mPolicy.enableScreenAfterBoot(); |
| |
| // Make sure the last requested orientation has been applied. |
| updateRotationUnchecked(false, false); |
| } |
| |
| private boolean checkBootAnimationCompleteLocked() { |
| if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) { |
| mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED); |
| mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED, |
| BOOT_ANIMATION_POLL_INTERVAL); |
| ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Waiting for anim complete"); |
| return false; |
| } |
| ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Animation complete!"); |
| return true; |
| } |
| |
| public void showBootMessage(final CharSequence msg, final boolean always) { |
| boolean first = false; |
| synchronized (mGlobalLock) { |
| ProtoLog.i(WM_DEBUG_BOOT, "showBootMessage: msg=%s always=%b" |
| + " mAllowBootMessages=%b mShowingBootMessages=%b" |
| + " mSystemBooted=%b. %s", msg, always, mAllowBootMessages, |
| mShowingBootMessages, mSystemBooted, |
| new RuntimeException("here").fillInStackTrace()); |
| if (!mAllowBootMessages) { |
| return; |
| } |
| if (!mShowingBootMessages) { |
| if (!always) { |
| return; |
| } |
| first = true; |
| } |
| if (mSystemBooted) { |
| return; |
| } |
| mShowingBootMessages = true; |
| mPolicy.showBootMessage(msg, always); |
| } |
| if (first) { |
| performEnableScreen(); |
| } |
| } |
| |
| public void hideBootMessagesLocked() { |
| ProtoLog.i(WM_DEBUG_BOOT, "hideBootMessagesLocked: mDisplayEnabled=%b" |
| + " mForceDisplayEnabled=%b mShowingBootMessages=%b" |
| + " mSystemBooted=%b. %s", mDisplayEnabled, mForceDisplayEnabled, |
| mShowingBootMessages, mSystemBooted, |
| new RuntimeException("here").fillInStackTrace()); |
| if (mShowingBootMessages) { |
| mShowingBootMessages = false; |
| mPolicy.hideBootMessages(); |
| } |
| } |
| |
| @Override |
| public void setInTouchMode(boolean mode) { |
| synchronized (mGlobalLock) { |
| mInTouchMode = mode; |
| } |
| mInputManager.setInTouchMode(mode); |
| } |
| |
| boolean getInTouchMode() { |
| synchronized (mGlobalLock) { |
| return mInTouchMode; |
| } |
| } |
| |
| public void showEmulatorDisplayOverlayIfNeeded() { |
| if (mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay) |
| && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false) |
| && Build.IS_EMULATOR) { |
| mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY)); |
| } |
| } |
| |
| public void showEmulatorDisplayOverlay() { |
| synchronized (mGlobalLock) { |
| |
| if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> showEmulatorDisplayOverlay"); |
| if (mEmulatorDisplayOverlay == null) { |
| mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mSurfaceFactory, mContext, |
| getDefaultDisplayContentLocked(), |
| mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER) |
| * TYPE_LAYER_MULTIPLIER + 10, mTransaction); |
| } |
| mEmulatorDisplayOverlay.setVisibility(true, mTransaction); |
| mTransaction.apply(); |
| } |
| } |
| |
| // TODO: more accounting of which pid(s) turned it on, keep count, |
| // only allow disables from pids which have count on, etc. |
| @Override |
| public void showStrictModeViolation(boolean on) { |
| final int pid = Binder.getCallingPid(); |
| if (on) { |
| // Show the visualization, and enqueue a second message to tear it |
| // down if we don't hear back from the app. |
| mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 1, pid)); |
| mH.sendMessageDelayed(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid), |
| DateUtils.SECOND_IN_MILLIS); |
| } else { |
| mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid)); |
| } |
| } |
| |
| private void showStrictModeViolation(int arg, int pid) { |
| final boolean on = arg != 0; |
| synchronized (mGlobalLock) { |
| // Ignoring requests to enable the red border from clients which aren't on screen. |
| // (e.g. Broadcast Receivers in the background..) |
| if (on && !mRoot.canShowStrictModeViolation(pid)) { |
| return; |
| } |
| |
| if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation"); |
| // TODO: Modify this to use the surface trace once it is not going crazy. |
| // b/31532461 |
| // TODO(multi-display): support multiple displays |
| if (mStrictModeFlash == null) { |
| mStrictModeFlash = new StrictModeFlash(mSurfaceFactory, |
| getDefaultDisplayContentLocked(), mTransaction); |
| } |
| mStrictModeFlash.setVisibility(on, mTransaction); |
| mTransaction.apply(); |
| } |
| } |
| |
| @Override |
| public void setStrictModeVisualIndicatorPreference(String value) { |
| SystemProperties.set(StrictMode.VISUAL_PROPERTY, value); |
| } |
| |
| @Override |
| public Bitmap screenshotWallpaper() { |
| if (!checkCallingPermission(READ_FRAME_BUFFER, "screenshotWallpaper()")) { |
| throw new SecurityException("Requires READ_FRAME_BUFFER permission"); |
| } |
| try { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper"); |
| synchronized (mGlobalLock) { |
| // TODO(b/115486823) Screenshot at secondary displays if needed. |
| final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY); |
| return dc.mWallpaperController.screenshotWallpaperLocked(); |
| } |
| } finally { |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| } |
| |
| /** |
| * Takes a snapshot of the screen. In landscape mode this grabs the whole screen. |
| * In portrait mode, it grabs the upper region of the screen based on the vertical dimension |
| * of the target image. |
| */ |
| @Override |
| public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) { |
| if (!checkCallingPermission(READ_FRAME_BUFFER, "requestAssistScreenshot()")) { |
| throw new SecurityException("Requires READ_FRAME_BUFFER permission"); |
| } |
| |
| final Bitmap bm; |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY); |
| if (displayContent == null) { |
| if (DEBUG_SCREENSHOT) { |
| Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId=" |
| + DEFAULT_DISPLAY); |
| } |
| bm = null; |
| } else { |
| bm = displayContent.screenshotDisplayLocked(Bitmap.Config.ARGB_8888); |
| } |
| } |
| |
| FgThread.getHandler().post(() -> { |
| try { |
| receiver.onHandleAssistScreenshot(bm); |
| } catch (RemoteException e) { |
| } |
| }); |
| |
| return true; |
| } |
| |
| public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution, |
| boolean restoreFromDisk) { |
| return mTaskSnapshotController.getSnapshot(taskId, userId, restoreFromDisk, |
| reducedResolution); |
| } |
| |
| /** |
| * In case a task write/delete operation was lost because the system crashed, this makes sure to |
| * clean up the directory to remove obsolete files. |
| * |
| * @param persistentTaskIds A set of task ids that exist in our in-memory model. |
| * @param runningUserIds The ids of the list of users that have tasks loaded in our in-memory |
| * model. |
| */ |
| public void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) { |
| synchronized (mGlobalLock) { |
| mTaskSnapshotController.removeObsoleteTaskFiles(persistentTaskIds, runningUserIds); |
| } |
| } |
| |
| @Override |
| public void setFixedToUserRotation(int displayId, int fixedToUserRotation) { |
| if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, |
| "freezeRotation()")) { |
| throw new SecurityException("Requires SET_ORIENTATION permission"); |
| } |
| synchronized (mGlobalLock) { |
| final DisplayContent display = mRoot.getDisplayContent(displayId); |
| if (display == null) { |
| Slog.w(TAG, "Trying to set rotate for app for a missing display."); |
| return; |
| } |
| display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation); |
| } |
| } |
| |
| @Override |
| public void freezeRotation(int rotation) { |
| freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation); |
| } |
| |
| /** |
| * Freeze rotation changes. (Enable "rotation lock".) |
| * Persists across reboots. |
| * @param displayId The ID of the display to freeze. |
| * @param rotation The desired rotation to freeze to, or -1 to use the current rotation. |
| */ |
| @Override |
| public void freezeDisplayRotation(int displayId, int rotation) { |
| // TODO(multi-display): Track which display is rotated. |
| if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, |
| "freezeRotation()")) { |
| throw new SecurityException("Requires SET_ORIENTATION permission"); |
| } |
| if (rotation < -1 || rotation > Surface.ROTATION_270) { |
| throw new IllegalArgumentException("Rotation argument must be -1 or a valid " |
| + "rotation constant."); |
| } |
| |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent display = mRoot.getDisplayContent(displayId); |
| if (display == null) { |
| Slog.w(TAG, "Trying to freeze rotation for a missing display."); |
| return; |
| } |
| display.getDisplayRotation().freezeRotation(rotation); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| |
| updateRotationUnchecked(false, false); |
| } |
| |
| @Override |
| public void thawRotation() { |
| thawDisplayRotation(Display.DEFAULT_DISPLAY); |
| } |
| |
| /** |
| * Thaw rotation changes. (Disable "rotation lock".) |
| * Persists across reboots. |
| */ |
| @Override |
| public void thawDisplayRotation(int displayId) { |
| if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, |
| "thawRotation()")) { |
| throw new SecurityException("Requires SET_ORIENTATION permission"); |
| } |
| |
| ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation()); |
| |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent display = mRoot.getDisplayContent(displayId); |
| if (display == null) { |
| Slog.w(TAG, "Trying to thaw rotation for a missing display."); |
| return; |
| } |
| display.getDisplayRotation().thawRotation(); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| |
| updateRotationUnchecked(false, false); |
| } |
| |
| @Override |
| public boolean isRotationFrozen() { |
| return isDisplayRotationFrozen(Display.DEFAULT_DISPLAY); |
| } |
| |
| @Override |
| public boolean isDisplayRotationFrozen(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent display = mRoot.getDisplayContent(displayId); |
| if (display == null) { |
| Slog.w(TAG, "Trying to thaw rotation for a missing display."); |
| return false; |
| } |
| return display.getDisplayRotation().isRotationFrozen(); |
| } |
| } |
| |
| /** |
| * Recalculate the current rotation. |
| * |
| * Called by the window manager policy whenever the state of the system changes |
| * such that the current rotation might need to be updated, such as when the |
| * device is docked or rotated into a new posture. |
| */ |
| @Override |
| public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { |
| updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); |
| } |
| |
| private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { |
| ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:" |
| + " alwaysSendConfiguration=%b forceRelayout=%b", |
| alwaysSendConfiguration, forceRelayout); |
| |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation"); |
| |
| long origId = Binder.clearCallingIdentity(); |
| |
| try { |
| synchronized (mGlobalLock) { |
| boolean layoutNeeded = false; |
| final int displayCount = mRoot.mChildren.size(); |
| for (int i = 0; i < displayCount; ++i) { |
| final DisplayContent displayContent = mRoot.mChildren.get(i); |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display"); |
| final boolean rotationChanged = displayContent.updateRotationUnchecked(); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| |
| if (!rotationChanged || forceRelayout) { |
| displayContent.setLayoutNeeded(); |
| layoutNeeded = true; |
| } |
| if (rotationChanged || alwaysSendConfiguration) { |
| displayContent.sendNewConfiguration(); |
| } |
| } |
| |
| if (layoutNeeded) { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, |
| "updateRotation: performSurfacePlacement"); |
| mWindowPlacerLocked.performSurfacePlacement(); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| } |
| } |
| |
| @Override |
| public int getDefaultDisplayRotation() { |
| synchronized (mGlobalLock) { |
| return getDefaultDisplayContentLocked().getRotation(); |
| } |
| } |
| |
| @Override |
| public void setDisplayWindowRotationController(IDisplayWindowRotationController controller) { |
| if (mContext.checkCallingOrSelfPermission(MANAGE_ACTIVITY_STACKS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + MANAGE_ACTIVITY_STACKS); |
| } |
| try { |
| synchronized (mGlobalLock) { |
| if (mDisplayRotationController != null) { |
| mDisplayRotationController.asBinder().unlinkToDeath( |
| mDisplayRotationControllerDeath, 0); |
| mDisplayRotationController = null; |
| } |
| controller.asBinder().linkToDeath(mDisplayRotationControllerDeath, 0); |
| mDisplayRotationController = controller; |
| } |
| } catch (RemoteException e) { |
| throw new RuntimeException("Unable to set rotation controller"); |
| } |
| } |
| |
| @Override |
| public SurfaceControl addShellRoot(int displayId, IWindow client, int windowType) { |
| if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| return null; |
| } |
| return dc.addShellRoot(client, windowType); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public void setDisplayWindowInsetsController( |
| int displayId, IDisplayWindowInsetsController insetsController) { |
| if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| return; |
| } |
| dc.setRemoteInsetsController(insetsController); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public void modifyDisplayWindowInsets(int displayId, InsetsState state) { |
| if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null || dc.mRemoteInsetsControlTarget == null) { |
| return; |
| } |
| dc.getInsetsStateController().onInsetsModified( |
| dc.mRemoteInsetsControlTarget, state); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public int watchRotation(IRotationWatcher watcher, int displayId) { |
| final DisplayContent displayContent; |
| synchronized (mGlobalLock) { |
| displayContent = mRoot.getDisplayContent(displayId); |
| } |
| if (displayContent == null) { |
| throw new IllegalArgumentException("Trying to register rotation event " |
| + "for invalid display: " + displayId); |
| } |
| |
| final IBinder watcherBinder = watcher.asBinder(); |
| IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { |
| @Override |
| public void binderDied() { |
| synchronized (mGlobalLock) { |
| for (int i=0; i<mRotationWatchers.size(); i++) { |
| if (watcherBinder == mRotationWatchers.get(i).mWatcher.asBinder()) { |
| RotationWatcher removed = mRotationWatchers.remove(i); |
| IBinder binder = removed.mWatcher.asBinder(); |
| if (binder != null) { |
| binder.unlinkToDeath(this, 0); |
| } |
| i--; |
| } |
| } |
| } |
| } |
| }; |
| |
| synchronized (mGlobalLock) { |
| try { |
| watcher.asBinder().linkToDeath(dr, 0); |
| mRotationWatchers.add(new RotationWatcher(watcher, dr, displayId)); |
| } catch (RemoteException e) { |
| // Client died, no cleanup needed. |
| } |
| |
| return displayContent.getRotation(); |
| } |
| } |
| |
| @Override |
| public void removeRotationWatcher(IRotationWatcher watcher) { |
| final IBinder watcherBinder = watcher.asBinder(); |
| synchronized (mGlobalLock) { |
| for (int i=0; i<mRotationWatchers.size(); i++) { |
| RotationWatcher rotationWatcher = mRotationWatchers.get(i); |
| if (watcherBinder == rotationWatcher.mWatcher.asBinder()) { |
| RotationWatcher removed = mRotationWatchers.remove(i); |
| IBinder binder = removed.mWatcher.asBinder(); |
| if (binder != null) { |
| binder.unlinkToDeath(removed.mDeathRecipient, 0); |
| } |
| i--; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public boolean registerWallpaperVisibilityListener(IWallpaperVisibilityListener listener, |
| int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| throw new IllegalArgumentException("Trying to register visibility event " |
| + "for invalid display: " + displayId); |
| } |
| mWallpaperVisibilityListeners.registerWallpaperVisibilityListener(listener, displayId); |
| return displayContent.mWallpaperController.isWallpaperVisible(); |
| } |
| } |
| |
| @Override |
| public void unregisterWallpaperVisibilityListener(IWallpaperVisibilityListener listener, |
| int displayId) { |
| synchronized (mGlobalLock) { |
| mWallpaperVisibilityListeners |
| .unregisterWallpaperVisibilityListener(listener, displayId); |
| } |
| } |
| |
| @Override |
| public void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener, |
| int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| throw new IllegalArgumentException("Trying to register visibility event " |
| + "for invalid display: " + displayId); |
| } |
| displayContent.registerSystemGestureExclusionListener(listener); |
| } |
| } |
| |
| @Override |
| public void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener, |
| int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| throw new IllegalArgumentException("Trying to register visibility event " |
| + "for invalid display: " + displayId); |
| } |
| displayContent.unregisterSystemGestureExclusionListener(listener); |
| } |
| } |
| |
| void reportSystemGestureExclusionChanged(Session session, IWindow window, |
| List<Rect> exclusionRects) { |
| synchronized (mGlobalLock) { |
| final WindowState win = windowForClientLocked(session, window, true); |
| if (win.setSystemGestureExclusion(exclusionRects)) { |
| win.getDisplayContent().updateSystemGestureExclusion(); |
| } |
| } |
| } |
| |
| @Override |
| public void registerDisplayFoldListener(IDisplayFoldListener listener) { |
| mPolicy.registerDisplayFoldListener(listener); |
| } |
| |
| @Override |
| public void unregisterDisplayFoldListener(IDisplayFoldListener listener) { |
| mPolicy.unregisterDisplayFoldListener(listener); |
| } |
| |
| /** |
| * Overrides the folded area. |
| * |
| * @param area the overriding folded area or an empty {@code Rect} to clear the override. |
| */ |
| void setOverrideFoldedArea(@NonNull Rect area) { |
| if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); |
| } |
| |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| mPolicy.setOverrideFoldedArea(area); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| /** |
| * Get the display folded area. |
| */ |
| @NonNull Rect getFoldedArea() { |
| long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| return mPolicy.getFoldedArea(); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| /** Registers a hierarchy listener that gets callbacks when the hierarchy changes. */ |
| @Override |
| public void registerDisplayWindowListener(IDisplayWindowListener listener) { |
| if (mContext.checkCallingOrSelfPermission(MANAGE_ACTIVITY_STACKS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + MANAGE_ACTIVITY_STACKS); |
| } |
| long ident = Binder.clearCallingIdentity(); |
| try { |
| mDisplayNotificationController.registerListener(listener); |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| /** Unregister a hierarchy listener so that it stops receiving callbacks. */ |
| @Override |
| public void unregisterDisplayWindowListener(IDisplayWindowListener listener) { |
| if (mContext.checkCallingOrSelfPermission(MANAGE_ACTIVITY_STACKS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + MANAGE_ACTIVITY_STACKS); |
| } |
| mDisplayNotificationController.unregisterListener(listener); |
| } |
| |
| @Override |
| public int getPreferredOptionsPanelGravity(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| return Gravity.CENTER | Gravity.BOTTOM; |
| } |
| return displayContent.getPreferredOptionsPanelGravity(); |
| } |
| } |
| |
| /** |
| * Starts the view server on the specified port. |
| * |
| * @param port The port to listener to. |
| * |
| * @return True if the server was successfully started, false otherwise. |
| * |
| * @see com.android.server.wm.ViewServer |
| * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT |
| */ |
| @Override |
| public boolean startViewServer(int port) { |
| if (isSystemSecure()) { |
| return false; |
| } |
| |
| if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) { |
| return false; |
| } |
| |
| if (port < 1024) { |
| return false; |
| } |
| |
| if (mViewServer != null) { |
| if (!mViewServer.isRunning()) { |
| try { |
| return mViewServer.start(); |
| } catch (IOException e) { |
| ProtoLog.w(WM_ERROR, "View server did not start"); |
| } |
| } |
| return false; |
| } |
| |
| try { |
| mViewServer = new ViewServer(this, port); |
| return mViewServer.start(); |
| } catch (IOException e) { |
| ProtoLog.w(WM_ERROR, "View server did not start"); |
| } |
| return false; |
| } |
| |
| private boolean isSystemSecure() { |
| return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) && |
| "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); |
| } |
| |
| /** |
| * Stops the view server if it exists. |
| * |
| * @return True if the server stopped, false if it wasn't started or |
| * couldn't be stopped. |
| * |
| * @see com.android.server.wm.ViewServer |
| */ |
| @Override |
| public boolean stopViewServer() { |
| if (isSystemSecure()) { |
| return false; |
| } |
| |
| if (!checkCallingPermission(Manifest.permission.DUMP, "stopViewServer")) { |
| return false; |
| } |
| |
| if (mViewServer != null) { |
| return mViewServer.stop(); |
| } |
| return false; |
| } |
| |
| /** |
| * Indicates whether the view server is running. |
| * |
| * @return True if the server is running, false otherwise. |
| * |
| * @see com.android.server.wm.ViewServer |
| */ |
| @Override |
| public boolean isViewServerRunning() { |
| if (isSystemSecure()) { |
| return false; |
| } |
| |
| if (!checkCallingPermission(Manifest.permission.DUMP, "isViewServerRunning")) { |
| return false; |
| } |
| |
| return mViewServer != null && mViewServer.isRunning(); |
| } |
| |
| /** |
| * Lists all available windows in the system. The listing is written in the specified Socket's |
| * output stream with the following syntax: windowHashCodeInHexadecimal windowName |
| * Each line of the output represents a different window. |
| * |
| * @param client The remote client to send the listing to. |
| * @return false if an error occurred, true otherwise. |
| */ |
| boolean viewServerListWindows(Socket client) { |
| if (isSystemSecure()) { |
| return false; |
| } |
| |
| boolean result = true; |
| |
| final ArrayList<WindowState> windows = new ArrayList(); |
| synchronized (mGlobalLock) { |
| mRoot.forAllWindows(w -> { |
| windows.add(w); |
| }, false /* traverseTopToBottom */); |
| } |
| |
| BufferedWriter out = null; |
| |
| // Any uncaught exception will crash the system process |
| try { |
| OutputStream clientStream = client.getOutputStream(); |
| out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); |
| |
| final int count = windows.size(); |
| for (int i = 0; i < count; i++) { |
| final WindowState w = windows.get(i); |
| out.write(Integer.toHexString(System.identityHashCode(w))); |
| out.write(' '); |
| out.append(w.mAttrs.getTitle()); |
| out.write('\n'); |
| } |
| |
| out.write("DONE.\n"); |
| out.flush(); |
| } catch (Exception e) { |
| result = false; |
| } finally { |
| if (out != null) { |
| try { |
| out.close(); |
| } catch (IOException e) { |
| result = false; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| // TODO(multidisplay): Extend to multiple displays. |
| /** |
| * Returns the focused window in the following format: |
| * windowHashCodeInHexadecimal windowName |
| * |
| * @param client The remote client to send the listing to. |
| * @return False if an error occurred, true otherwise. |
| */ |
| boolean viewServerGetFocusedWindow(Socket client) { |
| if (isSystemSecure()) { |
| return false; |
| } |
| |
| boolean result = true; |
| |
| WindowState focusedWindow = getFocusedWindow(); |
| |
| BufferedWriter out = null; |
| |
| // Any uncaught exception will crash the system process |
| try { |
| OutputStream clientStream = client.getOutputStream(); |
| out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); |
| |
| if(focusedWindow != null) { |
| out.write(Integer.toHexString(System.identityHashCode(focusedWindow))); |
| out.write(' '); |
| out.append(focusedWindow.mAttrs.getTitle()); |
| } |
| out.write('\n'); |
| out.flush(); |
| } catch (Exception e) { |
| result = false; |
| } finally { |
| if (out != null) { |
| try { |
| out.close(); |
| } catch (IOException e) { |
| result = false; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Sends a command to a target window. The result of the command, if any, will be |
| * written in the output stream of the specified socket. |
| * |
| * The parameters must follow this syntax: |
| * windowHashcode extra |
| * |
| * Where XX is the length in characeters of the windowTitle. |
| * |
| * The first parameter is the target window. The window with the specified hashcode |
| * will be the target. If no target can be found, nothing happens. The extra parameters |
| * will be delivered to the target window and as parameters to the command itself. |
| * |
| * @param client The remote client to sent the result, if any, to. |
| * @param command The command to execute. |
| * @param parameters The command parameters. |
| * |
| * @return True if the command was successfully delivered, false otherwise. This does |
| * not indicate whether the command itself was successful. |
| */ |
| boolean viewServerWindowCommand(Socket client, String command, String parameters) { |
| if (isSystemSecure()) { |
| return false; |
| } |
| |
| boolean success = true; |
| Parcel data = null; |
| Parcel reply = null; |
| |
| BufferedWriter out = null; |
| |
| // Any uncaught exception will crash the system process |
| try { |
| // Find the hashcode of the window |
| int index = parameters.indexOf(' '); |
| if (index == -1) { |
| index = parameters.length(); |
| } |
| final String code = parameters.substring(0, index); |
| int hashCode = (int) Long.parseLong(code, 16); |
| |
| // Extract the command's parameter after the window description |
| if (index < parameters.length()) { |
| parameters = parameters.substring(index + 1); |
| } else { |
| parameters = ""; |
| } |
| |
| final WindowState window = findWindow(hashCode); |
| if (window == null) { |
| return false; |
| } |
| |
| data = Parcel.obtain(); |
| data.writeInterfaceToken("android.view.IWindow"); |
| data.writeString(command); |
| data.writeString(parameters); |
| data.writeInt(1); |
| ParcelFileDescriptor.fromSocket(client).writeToParcel(data, 0); |
| |
| reply = Parcel.obtain(); |
| |
| final IBinder binder = window.mClient.asBinder(); |
| // TODO: GET THE TRANSACTION CODE IN A SAFER MANNER |
| binder.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); |
| |
| reply.readException(); |
| |
| if (!client.isOutputShutdown()) { |
| out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); |
| out.write("DONE\n"); |
| out.flush(); |
| } |
| |
| } catch (Exception e) { |
| ProtoLog.w(WM_ERROR, "Could not send command %s with parameters %s. %s", command, |
| parameters, e); |
| success = false; |
| } finally { |
| if (data != null) { |
| data.recycle(); |
| } |
| if (reply != null) { |
| reply.recycle(); |
| } |
| if (out != null) { |
| try { |
| out.close(); |
| } catch (IOException e) { |
| |
| } |
| } |
| } |
| |
| return success; |
| } |
| |
| public void addWindowChangeListener(WindowChangeListener listener) { |
| synchronized (mGlobalLock) { |
| mWindowChangeListeners.add(listener); |
| } |
| } |
| |
| public void removeWindowChangeListener(WindowChangeListener listener) { |
| synchronized (mGlobalLock) { |
| mWindowChangeListeners.remove(listener); |
| } |
| } |
| |
| private void notifyWindowsChanged() { |
| WindowChangeListener[] windowChangeListeners; |
| synchronized (mGlobalLock) { |
| if(mWindowChangeListeners.isEmpty()) { |
| return; |
| } |
| windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()]; |
| windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners); |
| } |
| int N = windowChangeListeners.length; |
| for(int i = 0; i < N; i++) { |
| windowChangeListeners[i].windowsChanged(); |
| } |
| } |
| |
| private void notifyFocusChanged() { |
| WindowChangeListener[] windowChangeListeners; |
| synchronized (mGlobalLock) { |
| if(mWindowChangeListeners.isEmpty()) { |
| return; |
| } |
| windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()]; |
| windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners); |
| } |
| int N = windowChangeListeners.length; |
| for(int i = 0; i < N; i++) { |
| windowChangeListeners[i].focusChanged(); |
| } |
| } |
| |
| private WindowState findWindow(int hashCode) { |
| if (hashCode == -1) { |
| // TODO(multidisplay): Extend to multiple displays. |
| return getFocusedWindow(); |
| } |
| |
| synchronized (mGlobalLock) { |
| return mRoot.getWindow((w) -> System.identityHashCode(w) == hashCode); |
| } |
| } |
| |
| public Configuration computeNewConfiguration(int displayId) { |
| synchronized (mGlobalLock) { |
| return computeNewConfigurationLocked(displayId); |
| } |
| } |
| |
| private Configuration computeNewConfigurationLocked(int displayId) { |
| if (!mDisplayReady) { |
| return null; |
| } |
| final Configuration config = new Configuration(); |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| displayContent.computeScreenConfiguration(config); |
| return config; |
| } |
| |
| void notifyHardKeyboardStatusChange() { |
| final boolean available; |
| final WindowManagerInternal.OnHardKeyboardStatusChangeListener listener; |
| synchronized (mGlobalLock) { |
| listener = mHardKeyboardStatusChangeListener; |
| available = mHardKeyboardAvailable; |
| } |
| if (listener != null) { |
| listener.onHardKeyboardStatusChange(available); |
| } |
| } |
| |
| // ------------------------------------------------------------- |
| // Input Events and Focus Management |
| // ------------------------------------------------------------- |
| |
| final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this); |
| private boolean mEventDispatchingEnabled; |
| |
| @Override |
| public void setEventDispatching(boolean enabled) { |
| if (!checkCallingPermission(MANAGE_APP_TOKENS, "setEventDispatching()")) { |
| throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| mEventDispatchingEnabled = enabled; |
| if (mDisplayEnabled) { |
| mInputManagerCallback.setEventDispatchingLw(enabled); |
| } |
| } |
| } |
| |
| private WindowState getFocusedWindow() { |
| synchronized (mGlobalLock) { |
| return getFocusedWindowLocked(); |
| } |
| } |
| |
| private WindowState getFocusedWindowLocked() { |
| // Return the focused window in the focused display. |
| return mRoot.getTopFocusedDisplayContent().mCurrentFocus; |
| } |
| |
| ActivityStack getImeFocusStackLocked() { |
| // Don't use mCurrentFocus.getStack() because it returns home stack for system windows. |
| // Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE |
| // and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved |
| // to make room for IME, but the window is not the focused window that's taking input. |
| // TODO (b/111080190): Consider the case of multiple IMEs on multi-display. |
| final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent(); |
| final ActivityRecord focusedApp = topFocusedDisplay.mFocusedApp; |
| return (focusedApp != null && focusedApp.getTask() != null) |
| ? focusedApp.getTask().getStack() : null; |
| } |
| |
| public boolean detectSafeMode() { |
| if (!mInputManagerCallback.waitForInputDevicesReady( |
| INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) { |
| ProtoLog.w(WM_ERROR, "Devices still not ready after waiting %d" |
| + " milliseconds before attempting to detect safe mode.", |
| INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS); |
| } |
| |
| if (Settings.Global.getInt( |
| mContext.getContentResolver(), Settings.Global.SAFE_BOOT_DISALLOWED, 0) != 0) { |
| return false; |
| } |
| |
| int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, |
| KeyEvent.KEYCODE_MENU); |
| int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S); |
| int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, |
| KeyEvent.KEYCODE_DPAD_CENTER); |
| int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, |
| InputManagerService.BTN_MOUSE); |
| int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, |
| KeyEvent.KEYCODE_VOLUME_DOWN); |
| mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0 |
| || volumeDownState > 0; |
| try { |
| if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0 |
| || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) { |
| mSafeMode = true; |
| SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, ""); |
| } |
| } catch (IllegalArgumentException e) { |
| } |
| if (mSafeMode) { |
| ProtoLog.i(WM_ERROR, "SAFE MODE ENABLED (menu=%d s=%d dpad=%d" |
| + " trackball=%d)", menuState, sState, dpadState, trackballState); |
| // May already be set if (for instance) this process has crashed |
| if (SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) == 0) { |
| SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1"); |
| } |
| } else { |
| ProtoLog.i(WM_ERROR, "SAFE MODE not enabled"); |
| } |
| mPolicy.setSafeMode(mSafeMode); |
| return mSafeMode; |
| } |
| |
| public void displayReady() { |
| synchronized (mGlobalLock) { |
| if (mMaxUiWidth > 0) { |
| mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth)); |
| } |
| applyForcedPropertiesForDefaultDisplay(); |
| mAnimator.ready(); |
| mDisplayReady = true; |
| // Reconfigure all displays to make sure that forced properties and |
| // DisplayWindowSettings are applied. |
| mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked); |
| mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_TOUCHSCREEN); |
| } |
| |
| try { |
| mActivityTaskManager.updateConfiguration(null); |
| } catch (RemoteException e) { |
| } |
| } |
| |
| public void systemReady() { |
| mSystemReady = true; |
| mPolicy.systemReady(); |
| mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady); |
| mTaskSnapshotController.systemReady(); |
| mHasWideColorGamutSupport = queryWideColorGamutSupport(); |
| mHasHdrSupport = queryHdrSupport(); |
| UiThread.getHandler().post(mSettingsObserver::updateSystemUiSettings); |
| UiThread.getHandler().post(mSettingsObserver::updatePointerLocation); |
| IVrManager vrManager = IVrManager.Stub.asInterface( |
| ServiceManager.getService(Context.VR_SERVICE)); |
| if (vrManager != null) { |
| try { |
| final boolean vrModeEnabled = vrManager.getVrModeState(); |
| synchronized (mGlobalLock) { |
| vrManager.registerListener(mVrStateCallbacks); |
| if (vrModeEnabled) { |
| mVrModeEnabled = vrModeEnabled; |
| mVrStateCallbacks.onVrStateChanged(vrModeEnabled); |
| } |
| } |
| } catch (RemoteException e) { |
| // Ignore, we cannot do anything if we failed to register VR mode listener |
| } |
| } |
| } |
| |
| private static boolean queryWideColorGamutSupport() { |
| try { |
| ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); |
| OptionalBool hasWideColor = surfaceFlinger.hasWideColorDisplay(); |
| if (hasWideColor != null) { |
| return hasWideColor.value; |
| } |
| } catch (RemoteException e) { |
| // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store |
| } |
| return false; |
| } |
| |
| private static boolean queryHdrSupport() { |
| try { |
| ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); |
| OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay(); |
| if (hasHdr != null) { |
| return hasHdr.value; |
| } |
| } catch (RemoteException e) { |
| // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store |
| } |
| return false; |
| } |
| |
| // ------------------------------------------------------------- |
| // Async Handler |
| // ------------------------------------------------------------- |
| |
| final class H extends android.os.Handler { |
| public static final int REPORT_FOCUS_CHANGE = 2; |
| public static final int REPORT_LOSING_FOCUS = 3; |
| public static final int WINDOW_FREEZE_TIMEOUT = 11; |
| |
| public static final int PERSIST_ANIMATION_SCALE = 14; |
| public static final int FORCE_GC = 15; |
| public static final int ENABLE_SCREEN = 16; |
| public static final int APP_FREEZE_TIMEOUT = 17; |
| public static final int REPORT_WINDOWS_CHANGE = 19; |
| |
| public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22; |
| public static final int BOOT_TIMEOUT = 23; |
| public static final int WAITING_FOR_DRAWN_TIMEOUT = 24; |
| public static final int SHOW_STRICT_MODE_VIOLATION = 25; |
| |
| public static final int CLIENT_FREEZE_TIMEOUT = 30; |
| public static final int NOTIFY_ACTIVITY_DRAWN = 32; |
| |
| public static final int ALL_WINDOWS_DRAWN = 33; |
| |
| public static final int NEW_ANIMATOR_SCALE = 34; |
| |
| public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36; |
| |
| public static final int CHECK_IF_BOOT_ANIMATION_FINISHED = 37; |
| public static final int RESET_ANR_MESSAGE = 38; |
| public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39; |
| |
| public static final int UPDATE_DOCKED_STACK_DIVIDER = 41; |
| |
| public static final int WINDOW_REPLACEMENT_TIMEOUT = 46; |
| |
| public static final int UPDATE_ANIMATION_SCALE = 51; |
| public static final int WINDOW_HIDE_TIMEOUT = 52; |
| public static final int SEAMLESS_ROTATION_TIMEOUT = 54; |
| public static final int RESTORE_POINTER_ICON = 55; |
| public static final int SET_HAS_OVERLAY_UI = 58; |
| public static final int ANIMATION_FAILSAFE = 60; |
| public static final int RECOMPUTE_FOCUS = 61; |
| public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62; |
| public static final int LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED = 63; |
| |
| /** |
| * Used to denote that an integer field in a message will not be used. |
| */ |
| public static final int UNUSED = 0; |
| |
| @Override |
| public void handleMessage(Message msg) { |
| if (DEBUG_WINDOW_TRACE) { |
| Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what); |
| } |
| switch (msg.what) { |
| case REPORT_FOCUS_CHANGE: { |
| final DisplayContent displayContent = (DisplayContent) msg.obj; |
| WindowState lastFocus; |
| WindowState newFocus; |
| |
| AccessibilityController accessibilityController = null; |
| |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController != null) { |
| accessibilityController = mAccessibilityController; |
| } |
| |
| lastFocus = displayContent.mLastFocus; |
| newFocus = displayContent.mCurrentFocus; |
| if (lastFocus == newFocus) { |
| // Focus is not changing, so nothing to do. |
| return; |
| } |
| displayContent.mLastFocus = newFocus; |
| ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s" |
| + " to %s displayId=%d", lastFocus, newFocus, |
| displayContent.getDisplayId()); |
| if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) { |
| ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Delaying loss of focus..."); |
| displayContent.mLosingFocus.add(lastFocus); |
| lastFocus = null; |
| } |
| } |
| |
| // First notify the accessibility manager for the change so it has |
| // the windows before the newly focused one starts firing events. |
| if (accessibilityController != null) { |
| accessibilityController.onWindowFocusChangedNotLocked( |
| displayContent.getDisplayId()); |
| } |
| |
| if (newFocus != null) { |
| ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus); |
| newFocus.reportFocusChangedSerialized(true); |
| notifyFocusChanged(); |
| } |
| |
| if (lastFocus != null) { |
| ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus); |
| lastFocus.reportFocusChangedSerialized(false); |
| } |
| break; |
| } |
| |
| case REPORT_LOSING_FOCUS: { |
| final DisplayContent displayContent = (DisplayContent) msg.obj; |
| ArrayList<WindowState> losers; |
| |
| synchronized (mGlobalLock) { |
| losers = displayContent.mLosingFocus; |
| displayContent.mLosingFocus = new ArrayList<>(); |
| } |
| |
| final int N = losers.size(); |
| for (int i = 0; i < N; i++) { |
| ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s", |
| losers.get(i)); |
| losers.get(i).reportFocusChangedSerialized(false); |
| } |
| break; |
| } |
| |
| case WINDOW_FREEZE_TIMEOUT: { |
| final DisplayContent displayContent = (DisplayContent) msg.obj; |
| synchronized (mGlobalLock) { |
| displayContent.onWindowFreezeTimeout(); |
| } |
| break; |
| } |
| |
| case PERSIST_ANIMATION_SCALE: { |
| Settings.Global.putFloat(mContext.getContentResolver(), |
| Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting); |
| Settings.Global.putFloat(mContext.getContentResolver(), |
| Settings.Global.TRANSITION_ANIMATION_SCALE, |
| mTransitionAnimationScaleSetting); |
| Settings.Global.putFloat(mContext.getContentResolver(), |
| Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting); |
| break; |
| } |
| |
| case UPDATE_ANIMATION_SCALE: { |
| @UpdateAnimationScaleMode |
| final int mode = msg.arg1; |
| switch (mode) { |
| case WINDOW_ANIMATION_SCALE: { |
| mWindowAnimationScaleSetting = Settings.Global.getFloat( |
| mContext.getContentResolver(), |
| Settings.Global.WINDOW_ANIMATION_SCALE, |
| mWindowAnimationScaleSetting); |
| break; |
| } |
| case TRANSITION_ANIMATION_SCALE: { |
| mTransitionAnimationScaleSetting = Settings.Global.getFloat( |
| mContext.getContentResolver(), |
| Settings.Global.TRANSITION_ANIMATION_SCALE, |
| mTransitionAnimationScaleSetting); |
| break; |
| } |
| case ANIMATION_DURATION_SCALE: { |
| mAnimatorDurationScaleSetting = Settings.Global.getFloat( |
| mContext.getContentResolver(), |
| Settings.Global.ANIMATOR_DURATION_SCALE, |
| mAnimatorDurationScaleSetting); |
| dispatchNewAnimatorScaleLocked(null); |
| break; |
| } |
| } |
| break; |
| } |
| |
| case FORCE_GC: { |
| synchronized (mGlobalLock) { |
| // Since we're holding both mWindowMap and mAnimator we don't need to |
| // hold mAnimator.mLayoutToAnim. |
| if (mAnimator.isAnimating() || mAnimator.isAnimationScheduled()) { |
| // If we are animating, don't do the gc now but |
| // delay a bit so we don't interrupt the animation. |
| sendEmptyMessageDelayed(H.FORCE_GC, 2000); |
| return; |
| } |
| // If we are currently rotating the display, it will |
| // schedule a new message when done. |
| if (mDisplayFrozen) { |
| return; |
| } |
| } |
| Runtime.getRuntime().gc(); |
| break; |
| } |
| |
| case ENABLE_SCREEN: { |
| performEnableScreen(); |
| break; |
| } |
| |
| case APP_FREEZE_TIMEOUT: { |
| synchronized (mGlobalLock) { |
| ProtoLog.w(WM_ERROR, "App freeze timeout expired."); |
| mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; |
| for (int i = mAppFreezeListeners.size() - 1; i >= 0; --i) { |
| mAppFreezeListeners.get(i).onAppFreezeTimeout(); |
| } |
| } |
| break; |
| } |
| |
| case CLIENT_FREEZE_TIMEOUT: { |
| synchronized (mGlobalLock) { |
| if (mClientFreezingScreen) { |
| mClientFreezingScreen = false; |
| mLastFinishedFreezeSource = "client-timeout"; |
| stopFreezingDisplayLocked(); |
| } |
| } |
| break; |
| } |
| |
| case REPORT_WINDOWS_CHANGE: { |
| if (mWindowsChanged) { |
| synchronized (mGlobalLock) { |
| mWindowsChanged = false; |
| } |
| notifyWindowsChanged(); |
| } |
| break; |
| } |
| |
| case REPORT_HARD_KEYBOARD_STATUS_CHANGE: { |
| notifyHardKeyboardStatusChange(); |
| break; |
| } |
| |
| case BOOT_TIMEOUT: { |
| performBootTimeout(); |
| break; |
| } |
| |
| case WAITING_FOR_DRAWN_TIMEOUT: { |
| Runnable callback = null; |
| final WindowContainer container = (WindowContainer) msg.obj; |
| synchronized (mGlobalLock) { |
| ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s", |
| container.mWaitingForDrawn); |
| container.mWaitingForDrawn.clear(); |
| callback = mWaitingForDrawnCallbacks.remove(container); |
| } |
| if (callback != null) { |
| callback.run(); |
| } |
| break; |
| } |
| |
| case SHOW_STRICT_MODE_VIOLATION: { |
| showStrictModeViolation(msg.arg1, msg.arg2); |
| break; |
| } |
| |
| case SHOW_EMULATOR_DISPLAY_OVERLAY: { |
| showEmulatorDisplayOverlay(); |
| break; |
| } |
| |
| case NOTIFY_ACTIVITY_DRAWN: { |
| try { |
| mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj); |
| } catch (RemoteException e) { |
| } |
| break; |
| } |
| case ALL_WINDOWS_DRAWN: { |
| Runnable callback; |
| final WindowContainer container = (WindowContainer) msg.obj; |
| synchronized (mGlobalLock) { |
| callback = mWaitingForDrawnCallbacks.remove(container); |
| } |
| if (callback != null) { |
| callback.run(); |
| } |
| break; |
| } |
| case NEW_ANIMATOR_SCALE: { |
| float scale = getCurrentAnimatorScale(); |
| ValueAnimator.setDurationScale(scale); |
| Session session = (Session)msg.obj; |
| if (session != null) { |
| try { |
| session.mCallback.onAnimatorScaleChanged(scale); |
| } catch (RemoteException e) { |
| } |
| } else { |
| ArrayList<IWindowSessionCallback> callbacks |
| = new ArrayList<IWindowSessionCallback>(); |
| synchronized (mGlobalLock) { |
| for (int i=0; i<mSessions.size(); i++) { |
| callbacks.add(mSessions.valueAt(i).mCallback); |
| } |
| |
| } |
| for (int i=0; i<callbacks.size(); i++) { |
| try { |
| callbacks.get(i).onAnimatorScaleChanged(scale); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| break; |
| } |
| case CHECK_IF_BOOT_ANIMATION_FINISHED: { |
| final boolean bootAnimationComplete; |
| synchronized (mGlobalLock) { |
| ProtoLog.i(WM_DEBUG_BOOT, "CHECK_IF_BOOT_ANIMATION_FINISHED:"); |
| bootAnimationComplete = checkBootAnimationCompleteLocked(); |
| } |
| if (bootAnimationComplete) { |
| performEnableScreen(); |
| } |
| break; |
| } |
| case RESET_ANR_MESSAGE: { |
| synchronized (mGlobalLock) { |
| mLastANRState = null; |
| } |
| mAtmInternal.clearSavedANRState(); |
| break; |
| } |
| case WALLPAPER_DRAW_PENDING_TIMEOUT: { |
| synchronized (mGlobalLock) { |
| final WallpaperController wallpaperController = |
| (WallpaperController) msg.obj; |
| if (wallpaperController != null |
| && wallpaperController.processWallpaperDrawPendingTimeout()) { |
| mWindowPlacerLocked.performSurfacePlacement(); |
| } |
| } |
| break; |
| } |
| case UPDATE_DOCKED_STACK_DIVIDER: { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = getDefaultDisplayContentLocked(); |
| if (displayContent != null) { |
| displayContent.getDockedDividerController().reevaluateVisibility(false); |
| displayContent.adjustForImeIfNeeded(); |
| } |
| } |
| break; |
| } |
| case WINDOW_REPLACEMENT_TIMEOUT: { |
| synchronized (mGlobalLock) { |
| for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) { |
| final ActivityRecord activity = mWindowReplacementTimeouts.get(i); |
| activity.onWindowReplacementTimeout(); |
| } |
| mWindowReplacementTimeouts.clear(); |
| } |
| break; |
| } |
| case WINDOW_HIDE_TIMEOUT: { |
| final WindowState window = (WindowState) msg.obj; |
| synchronized (mGlobalLock) { |
| // TODO: This is all about fixing b/21693547 |
| // where partially initialized Toasts get stuck |
| // around and keep the screen on. We'd like |
| // to just remove the toast...but this can cause clients |
| // who miss the timeout due to normal circumstances (e.g. |
| // running under debugger) to crash (b/29105388). The windows will |
| // eventually be removed when the client process finishes. |
| // The best we can do for now is remove the FLAG_KEEP_SCREEN_ON |
| // and prevent the symptoms of b/21693547. Since apps don't |
| // support windows being removed under them we hide the window |
| // and it will be removed when the app dies. |
| window.mAttrs.flags &= ~FLAG_KEEP_SCREEN_ON; |
| window.hidePermanentlyLw(); |
| window.setDisplayLayoutNeeded(); |
| mWindowPlacerLocked.performSurfacePlacement(); |
| } |
| break; |
| } |
| case RESTORE_POINTER_ICON: { |
| synchronized (mGlobalLock) { |
| restorePointerIconLocked((DisplayContent)msg.obj, msg.arg1, msg.arg2); |
| } |
| break; |
| } |
| case SEAMLESS_ROTATION_TIMEOUT: { |
| final DisplayContent displayContent = (DisplayContent) msg.obj; |
| synchronized (mGlobalLock) { |
| displayContent.getDisplayRotation().onSeamlessRotationTimeout(); |
| } |
| break; |
| } |
| case SET_HAS_OVERLAY_UI: { |
| mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1); |
| break; |
| } |
| case ANIMATION_FAILSAFE: { |
| synchronized (mGlobalLock) { |
| if (mRecentsAnimationController != null) { |
| mRecentsAnimationController.scheduleFailsafe(); |
| } |
| } |
| break; |
| } |
| case RECOMPUTE_FOCUS: { |
| synchronized (mGlobalLock) { |
| updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, |
| true /* updateInputWindows */); |
| } |
| break; |
| } |
| case ON_POINTER_DOWN_OUTSIDE_FOCUS: { |
| synchronized (mGlobalLock) { |
| final IBinder touchedToken = (IBinder) msg.obj; |
| onPointerDownOutsideFocusLocked(touchedToken); |
| } |
| break; |
| } |
| case LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED: { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = (DisplayContent) msg.obj; |
| displayContent.layoutAndAssignWindowLayersIfNeeded(); |
| } |
| break; |
| } |
| } |
| if (DEBUG_WINDOW_TRACE) { |
| Slog.v(TAG_WM, "handleMessage: exit"); |
| } |
| } |
| |
| /** Remove the previous messages with the same 'what' and 'obj' then send the new one. */ |
| void sendNewMessageDelayed(int what, Object obj, long delayMillis) { |
| removeMessages(what, obj); |
| sendMessageDelayed(obtainMessage(what, obj), delayMillis); |
| } |
| } |
| |
| void destroyPreservedSurfaceLocked() { |
| for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) { |
| final WindowState w = mDestroyPreservedSurface.get(i); |
| w.mWinAnimator.destroyPreservedSurfaceLocked(); |
| } |
| mDestroyPreservedSurface.clear(); |
| } |
| |
| // ------------------------------------------------------------- |
| // IWindowManager API |
| // ------------------------------------------------------------- |
| |
| @Override |
| public IWindowSession openSession(IWindowSessionCallback callback) { |
| return new Session(this, callback); |
| } |
| |
| @Override |
| public void getInitialDisplaySize(int displayId, Point size) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { |
| size.x = displayContent.mInitialDisplayWidth; |
| size.y = displayContent.mInitialDisplayHeight; |
| } |
| } |
| } |
| |
| @Override |
| public void getBaseDisplaySize(int displayId, Point size) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { |
| size.x = displayContent.mBaseDisplayWidth; |
| size.y = displayContent.mBaseDisplayHeight; |
| } |
| } |
| } |
| |
| @Override |
| public void setForcedDisplaySize(int displayId, int width, int height) { |
| if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); |
| } |
| |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.setForcedSize(width, height); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| @Override |
| public void setForcedDisplayScalingMode(int displayId, int mode) { |
| if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); |
| } |
| |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.setForcedScalingMode(mode); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| /** The global settings only apply to default display. */ |
| private boolean applyForcedPropertiesForDefaultDisplay() { |
| boolean changed = false; |
| final DisplayContent displayContent = getDefaultDisplayContentLocked(); |
| // Display size. |
| String sizeStr = Settings.Global.getString(mContext.getContentResolver(), |
| Settings.Global.DISPLAY_SIZE_FORCED); |
| if (sizeStr == null || sizeStr.length() == 0) { |
| sizeStr = SystemProperties.get(SIZE_OVERRIDE, null); |
| } |
| if (sizeStr != null && sizeStr.length() > 0) { |
| final int pos = sizeStr.indexOf(','); |
| if (pos > 0 && sizeStr.lastIndexOf(',') == pos) { |
| int width, height; |
| try { |
| width = Integer.parseInt(sizeStr.substring(0, pos)); |
| height = Integer.parseInt(sizeStr.substring(pos + 1)); |
| if (displayContent.mBaseDisplayWidth != width |
| || displayContent.mBaseDisplayHeight != height) { |
| ProtoLog.i(WM_ERROR, "FORCED DISPLAY SIZE: %dx%d", width, height); |
| displayContent.updateBaseDisplayMetrics(width, height, |
| displayContent.mBaseDisplayDensity); |
| changed = true; |
| } |
| } catch (NumberFormatException ex) { |
| } |
| } |
| } |
| |
| // Display density. |
| final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId); |
| if (density != 0 && density != displayContent.mBaseDisplayDensity) { |
| displayContent.mBaseDisplayDensity = density; |
| changed = true; |
| } |
| |
| // Display scaling mode. |
| int mode = Settings.Global.getInt(mContext.getContentResolver(), |
| Settings.Global.DISPLAY_SCALING_FORCE, 0); |
| if (displayContent.mDisplayScalingDisabled != (mode != 0)) { |
| ProtoLog.i(WM_ERROR, "FORCED DISPLAY SCALING DISABLED"); |
| displayContent.mDisplayScalingDisabled = true; |
| changed = true; |
| } |
| return changed; |
| } |
| |
| @Override |
| public void clearForcedDisplaySize(int displayId) { |
| if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); |
| } |
| |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.setForcedSize(displayContent.mInitialDisplayWidth, |
| displayContent.mInitialDisplayHeight); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| @Override |
| public int getInitialDisplayDensity(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { |
| return displayContent.mInitialDisplayDensity; |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public int getBaseDisplayDensity(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { |
| return displayContent.mBaseDisplayDensity; |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public void setForcedDisplayDensityForUser(int displayId, int density, int userId) { |
| if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); |
| } |
| |
| final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), |
| Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser", |
| null); |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.setForcedDensity(density, targetUserId); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| @Override |
| public void clearForcedDisplayDensityForUser(int displayId, int userId) { |
| if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); |
| } |
| |
| final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), |
| Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser", |
| null); |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.setForcedDensity(displayContent.mInitialDisplayDensity, |
| callingUserId); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| /** |
| * @param userId the ID of the user |
| * @return the forced display density for the specified user, if set, or |
| * {@code 0} if not set |
| */ |
| private int getForcedDisplayDensityForUserLocked(int userId) { |
| String densityStr = Settings.Secure.getStringForUser(mContext.getContentResolver(), |
| Settings.Secure.DISPLAY_DENSITY_FORCED, userId); |
| if (densityStr == null || densityStr.length() == 0) { |
| densityStr = SystemProperties.get(DENSITY_OVERRIDE, null); |
| } |
| if (densityStr != null && densityStr.length() > 0) { |
| try { |
| return Integer.parseInt(densityStr); |
| } catch (NumberFormatException ex) { |
| } |
| } |
| return 0; |
| } |
| |
| @Override |
| public void startWindowTrace(){ |
| mWindowTracing.startTrace(null /* printwriter */); |
| } |
| |
| @Override |
| public void stopWindowTrace(){ |
| mWindowTracing.stopTrace(null /* printwriter */); |
| } |
| |
| @Override |
| public boolean isWindowTraceEnabled() { |
| return mWindowTracing.isEnabled(); |
| } |
| |
| // ------------------------------------------------------------- |
| // Internals |
| // ------------------------------------------------------------- |
| |
| final WindowState windowForClientLocked(Session session, IWindow client, boolean throwOnError) { |
| return windowForClientLocked(session, client.asBinder(), throwOnError); |
| } |
| |
| final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) { |
| WindowState win = mWindowMap.get(client); |
| if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win); |
| if (win == null) { |
| if (throwOnError) { |
| throw new IllegalArgumentException( |
| "Requested window " + client + " does not exist"); |
| } |
| ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3)); |
| return null; |
| } |
| if (session != null && win.mSession != session) { |
| if (throwOnError) { |
| throw new IllegalArgumentException("Requested window " + client + " is in session " |
| + win.mSession + ", not " + session); |
| } |
| ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3)); |
| return null; |
| } |
| |
| return win; |
| } |
| |
| void makeWindowFreezingScreenIfNeededLocked(WindowState w) { |
| // If the screen is currently frozen or off, then keep |
| // it frozen/off until this window draws at its new |
| // orientation. |
| if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { |
| ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w); |
| w.setOrientationChanging(true); |
| w.mLastFreezeDuration = 0; |
| mRoot.mOrientationChangeComplete = false; |
| if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) { |
| mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; |
| // XXX should probably keep timeout from |
| // when we first froze the display. |
| mH.sendNewMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, w.getDisplayContent(), |
| WINDOW_FREEZE_TIMEOUT_DURATION); |
| } |
| } |
| } |
| |
| void checkDrawnWindowsLocked() { |
| if (mWaitingForDrawnCallbacks.isEmpty()) { |
| return; |
| } |
| mWaitingForDrawnCallbacks.forEach((container, callback) -> { |
| for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) { |
| final WindowState win = (WindowState) container.mWaitingForDrawn.get(j); |
| ProtoLog.i(WM_DEBUG_SCREEN_ON, |
| "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", |
| win, win.mRemoved, win.isVisibleLw(), win.mHasSurface, |
| win.mWinAnimator.mDrawState); |
| if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) { |
| // Window has been removed or hidden; no draw will now happen, so stop waiting. |
| ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win); |
| container.mWaitingForDrawn.remove(win); |
| } else if (win.hasDrawnLw()) { |
| // Window is now drawn (and shown). |
| ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win); |
| container.mWaitingForDrawn.remove(win); |
| } |
| } |
| if (container.mWaitingForDrawn.isEmpty()) { |
| ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!"); |
| mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container); |
| mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container)); |
| } |
| }); |
| } |
| |
| void setHoldScreenLocked(final Session newHoldScreen) { |
| final boolean hold = newHoldScreen != null; |
| |
| if (hold && mHoldingScreenOn != newHoldScreen) { |
| mHoldingScreenWakeLock.setWorkSource(new WorkSource(newHoldScreen.mUid)); |
| } |
| mHoldingScreenOn = newHoldScreen; |
| |
| final boolean state = mHoldingScreenWakeLock.isHeld(); |
| if (hold != state) { |
| if (hold) { |
| ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "Acquiring screen wakelock due to %s", |
| mRoot.mHoldScreenWindow); |
| mLastWakeLockHoldingWindow = mRoot.mHoldScreenWindow; |
| mLastWakeLockObscuringWindow = null; |
| mHoldingScreenWakeLock.acquire(); |
| mPolicy.keepScreenOnStartedLw(); |
| } else { |
| ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "Releasing screen wakelock, obscured by %s", |
| mRoot.mObscuringWindow); |
| mLastWakeLockHoldingWindow = null; |
| mLastWakeLockObscuringWindow = mRoot.mObscuringWindow; |
| mPolicy.keepScreenOnStoppedLw(); |
| mHoldingScreenWakeLock.release(); |
| } |
| } |
| } |
| |
| void requestTraversal() { |
| mWindowPlacerLocked.requestTraversal(); |
| } |
| |
| /** Note that Locked in this case is on mLayoutToAnim */ |
| void scheduleAnimationLocked() { |
| if (mAnimator != null) { |
| mAnimator.scheduleAnimation(); |
| } |
| } |
| |
| boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { |
| Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus"); |
| boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows); |
| Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); |
| return changed; |
| } |
| |
| void startFreezingDisplayLocked(int exitAnim, int enterAnim) { |
| startFreezingDisplayLocked(exitAnim, enterAnim, |
| getDefaultDisplayContentLocked()); |
| } |
| |
| void startFreezingDisplayLocked(int exitAnim, int enterAnim, |
| DisplayContent displayContent) { |
| if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) { |
| return; |
| } |
| |
| if (!displayContent.isReady() || !mPolicy.isScreenOn() || !displayContent.okToAnimate()) { |
| // No need to freeze the screen before the display is ready, if the screen is off, |
| // or we can't currently animate. |
| return; |
| } |
| |
| ProtoLog.d(WM_DEBUG_ORIENTATION, |
| "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s", |
| exitAnim, enterAnim, Debug.getCallers(8)); |
| mScreenFrozenLock.acquire(); |
| |
| mDisplayFrozen = true; |
| mDisplayFreezeTime = SystemClock.elapsedRealtime(); |
| mLastFinishedFreezeSource = null; |
| |
| // {@link mDisplayFrozen} prevents us from freezing on multiple displays at the same time. |
| // As a result, we only track the display that has initially froze the screen. |
| mFrozenDisplayId = displayContent.getDisplayId(); |
| |
| mInputManagerCallback.freezeInputDispatchingLw(); |
| |
| if (displayContent.mAppTransition.isTransitionSet()) { |
| displayContent.mAppTransition.freeze(); |
| } |
| |
| if (PROFILE_ORIENTATION) { |
| File file = new File("/data/system/frozen"); |
| Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); |
| } |
| |
| mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN); |
| mExitAnimId = exitAnim; |
| mEnterAnimId = enterAnim; |
| ScreenRotationAnimation screenRotationAnimation = |
| displayContent.getRotationAnimation(); |
| if (screenRotationAnimation != null) { |
| screenRotationAnimation.kill(); |
| } |
| |
| // Check whether the current screen contains any secure content. |
| boolean isSecure = displayContent.hasSecureWindowOnScreen(); |
| |
| displayContent.updateDisplayInfo(); |
| screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent, |
| displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure, |
| this); |
| displayContent.setRotationAnimation(screenRotationAnimation); |
| } |
| |
| void stopFreezingDisplayLocked() { |
| if (!mDisplayFrozen) { |
| return; |
| } |
| |
| final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId); |
| final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig; |
| final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0; |
| if (waitingForConfig || mAppsFreezingScreen > 0 |
| || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE |
| || mClientFreezingScreen || numOpeningApps > 0) { |
| ProtoLog.d(WM_DEBUG_ORIENTATION, |
| "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, " |
| + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, " |
| + "mClientFreezingScreen=%b, mOpeningApps.size()=%d", |
| waitingForConfig, mAppsFreezingScreen, mWindowsFreezingScreen, |
| mClientFreezingScreen, numOpeningApps); |
| return; |
| } |
| |
| ProtoLog.d(WM_DEBUG_ORIENTATION, |
| "stopFreezingDisplayLocked: Unfreezing now"); |
| |
| |
| // We must make a local copy of the displayId as it can be potentially overwritten later on |
| // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result |
| // of update rotation, but we reference the frozen display after that call in this method. |
| final int displayId = mFrozenDisplayId; |
| mFrozenDisplayId = INVALID_DISPLAY; |
| mDisplayFrozen = false; |
| mInputManagerCallback.thawInputDispatchingLw(); |
| mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime); |
| StringBuilder sb = new StringBuilder(128); |
| sb.append("Screen frozen for "); |
| TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb); |
| if (mLastFinishedFreezeSource != null) { |
| sb.append(" due to "); |
| sb.append(mLastFinishedFreezeSource); |
| } |
| ProtoLog.i(WM_ERROR, "%s", sb.toString()); |
| mH.removeMessages(H.APP_FREEZE_TIMEOUT); |
| mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); |
| if (PROFILE_ORIENTATION) { |
| Debug.stopMethodTracing(); |
| } |
| |
| boolean updateRotation = false; |
| |
| ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null |
| : displayContent.getRotationAnimation(); |
| if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { |
| ProtoLog.i(WM_DEBUG_ORIENTATION, "**** Dismissing screen rotation animation"); |
| DisplayInfo displayInfo = displayContent.getDisplayInfo(); |
| // Get rotation animation again, with new top window |
| if (!displayContent.getDisplayRotation().validateRotationAnimation( |
| mExitAnimId, mEnterAnimId, false /* forceDefault */)) { |
| mExitAnimId = mEnterAnimId = 0; |
| } |
| if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION, |
| getTransitionAnimationScaleLocked(), displayInfo.logicalWidth, |
| displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) { |
| mTransaction.apply(); |
| } else { |
| screenRotationAnimation.kill(); |
| displayContent.setRotationAnimation(null); |
| updateRotation = true; |
| } |
| } else { |
| if (screenRotationAnimation != null) { |
| screenRotationAnimation.kill(); |
| displayContent.setRotationAnimation(null); |
| } |
| updateRotation = true; |
| } |
| |
| boolean configChanged; |
| |
| // While the display is frozen we don't re-compute the orientation |
| // to avoid inconsistent states. However, something interesting |
| // could have actually changed during that time so re-evaluate it |
| // now to catch that. |
| configChanged = displayContent != null && displayContent.updateOrientation(); |
| |
| // A little kludge: a lot could have happened while the |
| // display was frozen, so now that we are coming back we |
| // do a gc so that any remote references the system |
| // processes holds on others can be released if they are |
| // no longer needed. |
| mH.removeMessages(H.FORCE_GC); |
| mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000); |
| |
| mScreenFrozenLock.release(); |
| |
| if (updateRotation && displayContent != null) { |
| ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation"); |
| configChanged |= displayContent.updateRotationUnchecked(); |
| } |
| |
| if (configChanged) { |
| displayContent.sendNewConfiguration(); |
| } |
| mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN); |
| } |
| |
| static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps, |
| DisplayMetrics dm) { |
| if (index < tokens.length) { |
| String str = tokens[index]; |
| if (str != null && str.length() > 0) { |
| try { |
| int val = Integer.parseInt(str); |
| return val; |
| } catch (Exception e) { |
| } |
| } |
| } |
| if (defUnits == TypedValue.COMPLEX_UNIT_PX) { |
| return defDps; |
| } |
| int val = (int)TypedValue.applyDimension(defUnits, defDps, dm); |
| return val; |
| } |
| |
| void createWatermark() { |
| if (mWatermark != null) { |
| return; |
| } |
| |
| File file = new File("/system/etc/setup.conf"); |
| FileInputStream in = null; |
| DataInputStream ind = null; |
| try { |
| in = new FileInputStream(file); |
| ind = new DataInputStream(in); |
| String line = ind.readLine(); |
| if (line != null) { |
| String[] toks = line.split("%"); |
| if (toks != null && toks.length > 0) { |
| // TODO(multi-display): Show watermarks on secondary displays. |
| final DisplayContent displayContent = getDefaultDisplayContentLocked(); |
| mWatermark = new Watermark(mSurfaceFactory, displayContent, |
| displayContent.mRealDisplayMetrics, toks, mTransaction); |
| mTransaction.apply(); |
| } |
| } |
| } catch (FileNotFoundException e) { |
| } catch (IOException e) { |
| } finally { |
| if (ind != null) { |
| try { |
| ind.close(); |
| } catch (IOException e) { |
| } |
| } else if (in != null) { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void setRecentsVisibility(boolean visible) { |
| mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR, |
| "setRecentsVisibility()"); |
| synchronized (mGlobalLock) { |
| mPolicy.setRecentsVisibilityLw(visible); |
| } |
| } |
| |
| @Override |
| public void setPipVisibility(boolean visible) { |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Caller does not hold permission " |
| + android.Manifest.permission.STATUS_BAR); |
| } |
| |
| synchronized (mGlobalLock) { |
| mPolicy.setPipVisibilityLw(visible); |
| } |
| } |
| |
| @Override |
| public void statusBarVisibilityChanged(int displayId, int visibility) { |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Caller does not hold permission " |
| + android.Manifest.permission.STATUS_BAR); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.statusBarVisibilityChanged(visibility); |
| } else { |
| Slog.w(TAG, "statusBarVisibilityChanged with invalid displayId=" + displayId); |
| } |
| } |
| } |
| |
| @Override |
| public void hideTransientBars(int displayId) { |
| mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR, |
| "hideTransientBars()"); |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.hideTransientBars(); |
| } else { |
| Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId); |
| } |
| } |
| } |
| |
| @Override |
| public void setForceShowSystemBars(boolean show) { |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Caller does not hold permission " |
| + android.Manifest.permission.STATUS_BAR); |
| } |
| synchronized (mGlobalLock) { |
| final PooledConsumer c = PooledLambda.obtainConsumer( |
| DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show); |
| mRoot.forAllDisplayPolicies(c); |
| c.recycle(); |
| } |
| } |
| |
| public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Caller does not hold permission " |
| + android.Manifest.permission.STATUS_BAR); |
| } |
| |
| synchronized (mGlobalLock) { |
| mPolicy.setNavBarVirtualKeyHapticFeedbackEnabledLw(enabled); |
| } |
| } |
| |
| /** |
| * Used by ActivityManager to determine where to position an app with aspect ratio shorter then |
| * the screen is. |
| * @see DisplayPolicy#getNavBarPosition() |
| */ |
| @Override |
| @WindowManagerPolicy.NavigationBarPosition |
| public int getNavBarPosition(int displayId) { |
| synchronized (mGlobalLock) { |
| // Perform layout if it was scheduled before to make sure that we get correct nav bar |
| // position when doing rotations. |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId |
| + " callers=" + Debug.getCallers(3)); |
| return NAV_BAR_INVALID; |
| } |
| displayContent.performLayout(false /* initial */, |
| false /* updateInputWindows */); |
| return displayContent.getDisplayPolicy().getNavBarPosition(); |
| } |
| } |
| |
| @Override |
| public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, |
| InputEventReceiver.Factory inputEventReceiverFactory, int displayId) { |
| synchronized (mGlobalLock) { |
| DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| return displayContent.getInputMonitor().createInputConsumer(looper, name, |
| inputEventReceiverFactory); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| @Override |
| public void createInputConsumer(IBinder token, String name, int displayId, |
| InputChannel inputChannel) { |
| synchronized (mGlobalLock) { |
| DisplayContent display = mRoot.getDisplayContent(displayId); |
| if (display != null) { |
| display.getInputMonitor().createInputConsumer(token, name, inputChannel, |
| Binder.getCallingPid(), Binder.getCallingUserHandle()); |
| } |
| } |
| } |
| |
| @Override |
| public boolean destroyInputConsumer(String name, int displayId) { |
| synchronized (mGlobalLock) { |
| DisplayContent display = mRoot.getDisplayContent(displayId); |
| if (display != null) { |
| return display.getInputMonitor().destroyInputConsumer(name); |
| } |
| return false; |
| } |
| } |
| |
| @Override |
| public Region getCurrentImeTouchRegion() { |
| if (mContext.checkCallingOrSelfPermission(RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) { |
| throw new SecurityException("getCurrentImeTouchRegion is restricted to VR services"); |
| } |
| synchronized (mGlobalLock) { |
| final Region r = new Region(); |
| // TODO(b/111080190): this method is only return the recent focused IME touch region, |
| // For Multi-Session IME, will need to add API for given display Id to |
| // get the right IME touch region. |
| for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) { |
| final DisplayContent displayContent = mRoot.mChildren.get(i); |
| if (displayContent.mInputMethodWindow != null) { |
| displayContent.mInputMethodWindow.getTouchableRegion(r); |
| return r; |
| } |
| } |
| return r; |
| } |
| } |
| |
| @Override |
| public boolean hasNavigationBar(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| return false; |
| } |
| return dc.getDisplayPolicy().hasNavigationBar(); |
| } |
| } |
| |
| @Override |
| public void lockNow(Bundle options) { |
| mPolicy.lockNow(options); |
| } |
| |
| public void showRecentApps() { |
| mPolicy.showRecentApps(); |
| } |
| |
| @Override |
| public boolean isSafeModeEnabled() { |
| return mSafeMode; |
| } |
| |
| @Override |
| public boolean clearWindowContentFrameStats(IBinder token) { |
| if (!checkCallingPermission(Manifest.permission.FRAME_STATS, |
| "clearWindowContentFrameStats()")) { |
| throw new SecurityException("Requires FRAME_STATS permission"); |
| } |
| synchronized (mGlobalLock) { |
| WindowState windowState = mWindowMap.get(token); |
| if (windowState == null) { |
| return false; |
| } |
| WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController; |
| if (surfaceController == null) { |
| return false; |
| } |
| return surfaceController.clearWindowContentFrameStats(); |
| } |
| } |
| |
| @Override |
| public WindowContentFrameStats getWindowContentFrameStats(IBinder token) { |
| if (!checkCallingPermission(Manifest.permission.FRAME_STATS, |
| "getWindowContentFrameStats()")) { |
| throw new SecurityException("Requires FRAME_STATS permission"); |
| } |
| synchronized (mGlobalLock) { |
| WindowState windowState = mWindowMap.get(token); |
| if (windowState == null) { |
| return null; |
| } |
| WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController; |
| if (surfaceController == null) { |
| return null; |
| } |
| if (mTempWindowRenderStats == null) { |
| mTempWindowRenderStats = new WindowContentFrameStats(); |
| } |
| WindowContentFrameStats stats = mTempWindowRenderStats; |
| if (!surfaceController.getWindowContentFrameStats(stats)) { |
| return null; |
| } |
| return stats; |
| } |
| } |
| |
| private void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { |
| pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); |
| mPolicy.dump(" ", pw, args); |
| } |
| |
| private void dumpAnimatorLocked(PrintWriter pw, String[] args, boolean dumpAll) { |
| pw.println("WINDOW MANAGER ANIMATOR STATE (dumpsys window animator)"); |
| mAnimator.dumpLocked(pw, " ", dumpAll); |
| } |
| |
| private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { |
| pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); |
| mRoot.dumpTokens(pw, dumpAll); |
| } |
| |
| |
| private void dumpHighRefreshRateBlacklist(PrintWriter pw) { |
| pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)"); |
| mHighRefreshRateBlacklist.dump(pw); |
| } |
| |
| private void dumpTraceStatus(PrintWriter pw) { |
| pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); |
| pw.print(mWindowTracing.getStatus() + "\n"); |
| } |
| |
| private void dumpLogStatus(PrintWriter pw) { |
| pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)"); |
| pw.println(ProtoLogImpl.getSingleInstance().getStatus()); |
| } |
| |
| private void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) { |
| pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)"); |
| for (int i=0; i<mSessions.size(); i++) { |
| Session s = mSessions.valueAt(i); |
| pw.print(" Session "); pw.print(s); pw.println(':'); |
| s.dump(pw, " "); |
| } |
| } |
| |
| /** |
| * Write to a protocol buffer output stream. Protocol buffer message definition is at |
| * {@link com.android.server.wm.WindowManagerServiceDumpProto}. |
| * |
| * @param proto Stream to write the WindowContainer object to. |
| * @param logLevel Determines the amount of data to be written to the Protobuf. |
| */ |
| void dumpDebugLocked(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) { |
| mPolicy.dumpDebug(proto, POLICY); |
| mRoot.dumpDebug(proto, ROOT_WINDOW_CONTAINER, logLevel); |
| final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent(); |
| if (topFocusedDisplayContent.mCurrentFocus != null) { |
| topFocusedDisplayContent.mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW); |
| } |
| if (topFocusedDisplayContent.mFocusedApp != null) { |
| topFocusedDisplayContent.mFocusedApp.writeNameToProto(proto, FOCUSED_APP); |
| } |
| final WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); |
| if (imeWindow != null) { |
| imeWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW); |
| } |
| proto.write(DISPLAY_FROZEN, mDisplayFrozen); |
| final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); |
| proto.write(ROTATION, defaultDisplayContent.getRotation()); |
| proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation()); |
| proto.write(FOCUSED_DISPLAY_ID, topFocusedDisplayContent.getDisplayId()); |
| } |
| |
| private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll, |
| ArrayList<WindowState> windows) { |
| pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)"); |
| dumpWindowsNoHeaderLocked(pw, dumpAll, windows); |
| } |
| |
| private void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll, |
| ArrayList<WindowState> windows) { |
| mRoot.dumpWindowsNoHeader(pw, dumpAll, windows); |
| |
| if (!mHidingNonSystemOverlayWindows.isEmpty()) { |
| pw.println(); |
| pw.println(" Hiding System Alert Windows:"); |
| for (int i = mHidingNonSystemOverlayWindows.size() - 1; i >= 0; i--) { |
| final WindowState w = mHidingNonSystemOverlayWindows.get(i); |
| pw.print(" #"); pw.print(i); pw.print(' '); |
| pw.print(w); |
| if (dumpAll) { |
| pw.println(":"); |
| w.dump(pw, " ", true); |
| } else { |
| pw.println(); |
| } |
| } |
| } |
| if (mPendingRemove.size() > 0) { |
| pw.println(); |
| pw.println(" Remove pending for:"); |
| for (int i=mPendingRemove.size()-1; i>=0; i--) { |
| WindowState w = mPendingRemove.get(i); |
| if (windows == null || windows.contains(w)) { |
| pw.print(" Remove #"); pw.print(i); pw.print(' '); |
| pw.print(w); |
| if (dumpAll) { |
| pw.println(":"); |
| w.dump(pw, " ", true); |
| } else { |
| pw.println(); |
| } |
| } |
| } |
| } |
| if (mForceRemoves != null && mForceRemoves.size() > 0) { |
| pw.println(); |
| pw.println(" Windows force removing:"); |
| for (int i=mForceRemoves.size()-1; i>=0; i--) { |
| WindowState w = mForceRemoves.get(i); |
| pw.print(" Removing #"); pw.print(i); pw.print(' '); |
| pw.print(w); |
| if (dumpAll) { |
| pw.println(":"); |
| w.dump(pw, " ", true); |
| } else { |
| pw.println(); |
| } |
| } |
| } |
| if (mDestroySurface.size() > 0) { |
| pw.println(); |
| pw.println(" Windows waiting to destroy their surface:"); |
| for (int i=mDestroySurface.size()-1; i>=0; i--) { |
| WindowState w = mDestroySurface.get(i); |
| if (windows == null || windows.contains(w)) { |
| pw.print(" Destroy #"); pw.print(i); pw.print(' '); |
| pw.print(w); |
| if (dumpAll) { |
| pw.println(":"); |
| w.dump(pw, " ", true); |
| } else { |
| pw.println(); |
| } |
| } |
| } |
| } |
| if (mResizingWindows.size() > 0) { |
| pw.println(); |
| pw.println(" Windows waiting to resize:"); |
| for (int i=mResizingWindows.size()-1; i>=0; i--) { |
| WindowState w = mResizingWindows.get(i); |
| if (windows == null || windows.contains(w)) { |
| pw.print(" Resizing #"); pw.print(i); pw.print(' '); |
| pw.print(w); |
| if (dumpAll) { |
| pw.println(":"); |
| w.dump(pw, " ", true); |
| } else { |
| pw.println(); |
| } |
| } |
| } |
| } |
| if (!mWaitingForDrawnCallbacks.isEmpty()) { |
| pw.println(); |
| pw.println(" Clients waiting for these windows to be drawn:"); |
| mWaitingForDrawnCallbacks.forEach((wc, callback) -> { |
| pw.print(" WindowContainer "); |
| pw.println(wc.getName()); |
| for (int i = wc.mWaitingForDrawn.size() - 1; i >= 0; i--) { |
| final WindowState win = (WindowState) wc.mWaitingForDrawn.get(i); |
| pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win); |
| } |
| }); |
| |
| } |
| pw.println(); |
| pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration()); |
| pw.print(" mHasPermanentDpad="); pw.println(mHasPermanentDpad); |
| mRoot.dumpTopFocusedDisplayId(pw); |
| mRoot.forAllDisplays(dc -> { |
| final WindowState inputMethodTarget = dc.mInputMethodTarget; |
| if (inputMethodTarget != null) { |
| pw.print(" mInputMethodTarget in display# "); pw.print(dc.getDisplayId()); |
| pw.print(' '); pw.println(inputMethodTarget); |
| } |
| }); |
| pw.print(" mInTouchMode="); pw.println(mInTouchMode); |
| pw.print(" mLastDisplayFreezeDuration="); |
| TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); |
| if ( mLastFinishedFreezeSource != null) { |
| pw.print(" due to "); |
| pw.print(mLastFinishedFreezeSource); |
| } |
| pw.println(); |
| pw.print(" mLastWakeLockHoldingWindow=");pw.print(mLastWakeLockHoldingWindow); |
| pw.print(" mLastWakeLockObscuringWindow="); pw.print(mLastWakeLockObscuringWindow); |
| pw.println(); |
| |
| mInputManagerCallback.dump(pw, " "); |
| mTaskSnapshotController.dump(pw, " "); |
| |
| if (dumpAll) { |
| final WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); |
| if (imeWindow != null) { |
| pw.print(" mInputMethodWindow="); pw.println(imeWindow); |
| } |
| mWindowPlacerLocked.dump(pw, " "); |
| pw.print(" mSystemBooted="); pw.print(mSystemBooted); |
| pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled); |
| |
| mRoot.dumpLayoutNeededDisplayIds(pw); |
| |
| pw.print(" mTransactionSequence="); pw.println(mTransactionSequence); |
| pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen); |
| pw.print(" windows="); pw.print(mWindowsFreezingScreen); |
| pw.print(" client="); pw.print(mClientFreezingScreen); |
| pw.print(" apps="); pw.print(mAppsFreezingScreen); |
| final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); |
| pw.print(" mRotation="); pw.print(defaultDisplayContent.getRotation()); |
| pw.print(" mLastOrientation="); |
| pw.println(defaultDisplayContent.getLastOrientation()); |
| pw.print(" waitingForConfig="); |
| pw.println(defaultDisplayContent.mWaitingForConfig); |
| |
| pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled); |
| pw.print(" window="); pw.print(mWindowAnimationScaleSetting); |
| pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting); |
| pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting); |
| if (mRecentsAnimationController != null) { |
| pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController); |
| mRecentsAnimationController.dump(pw, " "); |
| } |
| PolicyControl.dump(" ", pw); |
| } |
| } |
| |
| private boolean dumpWindows(PrintWriter pw, String name, String[] args, int opti, |
| boolean dumpAll) { |
| final ArrayList<WindowState> windows = new ArrayList(); |
| if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) { |
| final boolean appsOnly = name.contains("apps"); |
| final boolean visibleOnly = name.contains("visible"); |
| synchronized (mGlobalLock) { |
| if (appsOnly) { |
| mRoot.dumpDisplayContents(pw); |
| } |
| |
| mRoot.forAllWindows((w) -> { |
| if ((!visibleOnly || w.mWinAnimator.getShown()) |
| && (!appsOnly || w.mActivityRecord != null)) { |
| windows.add(w); |
| } |
| }, true /* traverseTopToBottom */); |
| } |
| } else { |
| synchronized (mGlobalLock) { |
| mRoot.getWindowsByName(windows, name); |
| } |
| } |
| |
| if (windows.size() <= 0) { |
| return false; |
| } |
| |
| synchronized (mGlobalLock) { |
| dumpWindowsLocked(pw, dumpAll, windows); |
| } |
| return true; |
| } |
| |
| private void dumpLastANRLocked(PrintWriter pw) { |
| pw.println("WINDOW MANAGER LAST ANR (dumpsys window lastanr)"); |
| if (mLastANRState == null) { |
| pw.println(" <no ANR has occurred since boot>"); |
| } else { |
| pw.println(mLastANRState); |
| } |
| } |
| |
| /** |
| * Saves information about the state of the window manager at |
| * the time an ANR occurred before anything else in the system changes |
| * in response. |
| * |
| * @param activity The application that ANR'd, may be null. |
| * @param windowState The window that ANR'd, may be null. |
| * @param reason The reason for the ANR, may be null. |
| */ |
| void saveANRStateLocked(ActivityRecord activity, WindowState windowState, String reason) { |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new FastPrintWriter(sw, false, 1024); |
| pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date())); |
| if (activity != null) { |
| pw.println(" Application at fault: " + activity.stringName); |
| } |
| if (windowState != null) { |
| pw.println(" Window at fault: " + windowState.mAttrs.getTitle()); |
| } |
| if (reason != null) { |
| pw.println(" Reason: " + reason); |
| } |
| for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { |
| final DisplayContent dc = mRoot.getChildAt(i); |
| final int displayId = dc.getDisplayId(); |
| if (!dc.mWinAddedSinceNullFocus.isEmpty()) { |
| pw.println(" Windows added in display #" + displayId + " since null focus: " |
| + dc.mWinAddedSinceNullFocus); |
| } |
| if (!dc.mWinRemovedSinceNullFocus.isEmpty()) { |
| pw.println(" Windows removed in display #" + displayId + " since null focus: " |
| + dc.mWinRemovedSinceNullFocus); |
| } |
| } |
| pw.println(); |
| dumpWindowsNoHeaderLocked(pw, true, null); |
| pw.println(); |
| pw.println("Last ANR continued"); |
| mRoot.dumpDisplayContents(pw); |
| pw.close(); |
| mLastANRState = sw.toString(); |
| |
| mH.removeMessages(H.RESET_ANR_MESSAGE); |
| mH.sendEmptyMessageDelayed(H.RESET_ANR_MESSAGE, LAST_ANR_LIFETIME_DURATION_MSECS); |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| PriorityDump.dump(mPriorityDumper, fd, pw, args); |
| } |
| |
| private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) { |
| if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; |
| boolean dumpAll = false; |
| |
| int opti = 0; |
| while (opti < args.length) { |
| String opt = args[opti]; |
| if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { |
| break; |
| } |
| opti++; |
| if ("-a".equals(opt)) { |
| dumpAll = true; |
| } else if ("-h".equals(opt)) { |
| pw.println("Window manager dump options:"); |
| pw.println(" [-a] [-h] [cmd] ..."); |
| pw.println(" cmd may be one of:"); |
| pw.println(" l[astanr]: last ANR information"); |
| pw.println(" p[policy]: policy state"); |
| pw.println(" a[animator]: animator state"); |
| pw.println(" s[essions]: active sessions"); |
| pw.println(" surfaces: active surfaces (debugging enabled only)"); |
| pw.println(" d[isplays]: active display contents"); |
| pw.println(" t[okens]: token list"); |
| pw.println(" w[indows]: window list"); |
| pw.println(" trace: print trace status and write Winscope trace to file"); |
| pw.println(" cmd may also be a NAME to dump windows. NAME may"); |
| pw.println(" be a partial substring in a window name, a"); |
| pw.println(" Window hex object identifier, or"); |
| pw.println(" \"all\" for all windows, or"); |
| pw.println(" \"visible\" for the visible windows."); |
| pw.println(" \"visible-apps\" for the visible app windows."); |
| pw.println(" -a: include all available server state."); |
| pw.println(" --proto: output dump in protocol buffer format."); |
| return; |
| } else { |
| pw.println("Unknown argument: " + opt + "; use -h for help"); |
| } |
| } |
| |
| if (useProto) { |
| final ProtoOutputStream proto = new ProtoOutputStream(fd); |
| synchronized (mGlobalLock) { |
| dumpDebugLocked(proto, WindowTraceLogLevel.ALL); |
| } |
| proto.flush(); |
| return; |
| } |
| // Is the caller requesting to dump a particular piece of data? |
| if (opti < args.length) { |
| String cmd = args[opti]; |
| opti++; |
| if ("lastanr".equals(cmd) || "l".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpLastANRLocked(pw); |
| } |
| return; |
| } else if ("policy".equals(cmd) || "p".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpPolicyLocked(pw, args, true); |
| } |
| return; |
| } else if ("animator".equals(cmd) || "a".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpAnimatorLocked(pw, args, true); |
| } |
| return; |
| } else if ("sessions".equals(cmd) || "s".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpSessionsLocked(pw, true); |
| } |
| return; |
| } else if ("displays".equals(cmd) || "d".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| mRoot.dumpDisplayContents(pw); |
| } |
| return; |
| } else if ("tokens".equals(cmd) || "t".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpTokensLocked(pw, true); |
| } |
| return; |
| } else if ("windows".equals(cmd) || "w".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpWindowsLocked(pw, true, null); |
| } |
| return; |
| } else if ("all".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| dumpWindowsLocked(pw, true, null); |
| } |
| return; |
| } else if ("containers".equals(cmd)) { |
| synchronized (mGlobalLock) { |
| mRoot.dumpChildrenNames(pw, " "); |
| pw.println(" "); |
| mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */); |
| } |
| return; |
| } else if ("trace".equals(cmd)) { |
| dumpTraceStatus(pw); |
| return; |
| } else if ("logging".equals(cmd)) { |
| dumpLogStatus(pw); |
| return; |
| } else if ("refresh".equals(cmd)) { |
| dumpHighRefreshRateBlacklist(pw); |
| return; |
| } else if ("constants".equals(cmd)) { |
| mConstants.dump(pw); |
| return; |
| } else { |
| // Dumping a single name? |
| if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { |
| pw.println("Bad window command, or no windows match: " + cmd); |
| pw.println("Use -h for help."); |
| } |
| return; |
| } |
| } |
| |
| synchronized (mGlobalLock) { |
| pw.println(); |
| final String separator = "---------------------------------------------------------" |
| + "----------------------"; |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpLastANRLocked(pw); |
| pw.println(); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpPolicyLocked(pw, args, dumpAll); |
| pw.println(); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpAnimatorLocked(pw, args, dumpAll); |
| pw.println(); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpSessionsLocked(pw, dumpAll); |
| pw.println(); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| mRoot.dumpDisplayContents(pw); |
| pw.println(); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpTokensLocked(pw, dumpAll); |
| pw.println(); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpWindowsLocked(pw, dumpAll, null); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpTraceStatus(pw); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpLogStatus(pw); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| dumpHighRefreshRateBlacklist(pw); |
| if (dumpAll) { |
| pw.println(separator); |
| } |
| mConstants.dump(pw); |
| } |
| } |
| |
| // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection). |
| @Override |
| public void monitor() { |
| synchronized (mGlobalLock) { } |
| } |
| |
| // There is an inherent assumption that this will never return null. |
| // TODO(multi-display): Inspect all the call-points of this method to see if they make sense to |
| // support non-default display. |
| DisplayContent getDefaultDisplayContentLocked() { |
| return mRoot.getDisplayContent(DEFAULT_DISPLAY); |
| } |
| |
| public void onOverlayChanged() { |
| synchronized (mGlobalLock) { |
| mRoot.forAllDisplays(displayContent -> { |
| displayContent.getDisplayPolicy().onOverlayChangedLw(); |
| displayContent.updateDisplayInfo(); |
| }); |
| requestTraversal(); |
| } |
| } |
| |
| @Override |
| public Object getWindowManagerLock() { |
| return mGlobalLock; |
| } |
| |
| /** |
| * Hint to a token that its activity will relaunch, which will trigger removal and addition of |
| * a window. |
| * |
| * @param token Application token for which the activity will be relaunched. |
| */ |
| void setWillReplaceWindow(IBinder token, boolean animate) { |
| final ActivityRecord activity = mRoot.getActivityRecord(token); |
| if (activity == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to set replacing window on non-existing app token %s", |
| token); |
| return; |
| } |
| if (!activity.hasContentToDisplay()) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to set replacing window on app token with no content %s", |
| token); |
| return; |
| } |
| activity.setWillReplaceWindows(animate); |
| } |
| |
| /** |
| * Hint to a token that its windows will be replaced across activity relaunch. |
| * The windows would otherwise be removed shortly following this as the |
| * activity is torn down. |
| * @param token Application token for which the activity will be relaunched. |
| * @param childrenOnly Whether to mark only child windows for replacement |
| * (for the case where main windows are being preserved/ |
| * reused rather than replaced). |
| * |
| */ |
| // TODO: The s at the end of the method name is the only difference with the name of the method |
| // above. We should combine them or find better names. |
| void setWillReplaceWindows(IBinder token, boolean childrenOnly) { |
| synchronized (mGlobalLock) { |
| final ActivityRecord activity = mRoot.getActivityRecord(token); |
| if (activity == null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to set replacing window on non-existing app token %s", |
| token); |
| return; |
| } |
| if (!activity.hasContentToDisplay()) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to set replacing window on app token with no content %s", |
| token); |
| return; |
| } |
| |
| if (childrenOnly) { |
| activity.setWillReplaceChildWindows(); |
| } else { |
| activity.setWillReplaceWindows(false /* animate */); |
| } |
| |
| scheduleClearWillReplaceWindows(token, true /* replacing */); |
| } |
| } |
| |
| /** |
| * If we're replacing the window, schedule a timer to clear the replaced window |
| * after a timeout, in case the replacing window is not coming. |
| * |
| * If we're not replacing the window, clear the replace window settings of the app. |
| * |
| * @param token Application token for the activity whose window might be replaced. |
| * @param replacing Whether the window is being replaced or not. |
| */ |
| void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) { |
| final ActivityRecord activity = mRoot.getActivityRecord(token); |
| if (activity == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to reset replacing window on non-existing app token %s", |
| token); |
| return; |
| } |
| if (replacing) { |
| scheduleWindowReplacementTimeouts(activity); |
| } else { |
| activity.clearWillReplaceWindows(); |
| } |
| } |
| |
| void scheduleWindowReplacementTimeouts(ActivityRecord activity) { |
| if (!mWindowReplacementTimeouts.contains(activity)) { |
| mWindowReplacementTimeouts.add(activity); |
| } |
| mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT); |
| mH.sendEmptyMessageDelayed( |
| H.WINDOW_REPLACEMENT_TIMEOUT, WINDOW_REPLACEMENT_TIMEOUT_DURATION); |
| } |
| |
| @Override |
| public int getDockedStackSide() { |
| synchronized (mGlobalLock) { |
| final ActivityStack dockedStack = getDefaultDisplayContentLocked() |
| .getRootSplitScreenPrimaryTask(); |
| return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(); |
| } |
| } |
| |
| void setDockedStackResizing(boolean resizing) { |
| getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing); |
| requestTraversal(); |
| } |
| |
| @Override |
| public void setDockedStackDividerTouchRegion(Rect touchRegion) { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = getDefaultDisplayContentLocked(); |
| dc.getDockedDividerController().setTouchRegion(touchRegion); |
| dc.updateTouchExcludeRegion(); |
| } |
| } |
| |
| @Override |
| public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { |
| synchronized (mGlobalLock) { |
| getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer( |
| visible, targetWindowingMode, alpha); |
| } |
| } |
| |
| void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) { |
| synchronized (mGlobalLock) { |
| mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays; |
| } |
| } |
| |
| @VisibleForTesting |
| void setIsPc(boolean isPc) { |
| synchronized (mGlobalLock) { |
| mIsPc = isPc; |
| } |
| } |
| |
| static int dipToPixel(int dip, DisplayMetrics displayMetrics) { |
| return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); |
| } |
| |
| @Override |
| public void registerDockedStackListener(IDockedStackListener listener) { |
| mAtmInternal.enforceCallerIsRecentsOrHasPermission(REGISTER_WINDOW_MANAGER_LISTENERS, |
| "registerDockedStackListener()"); |
| synchronized (mGlobalLock) { |
| // TODO(multi-display): The listener is registered on the default display only. |
| getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener( |
| listener); |
| } |
| } |
| |
| @Override |
| public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) { |
| if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, |
| "registerPinnedStackListener()")) { |
| return; |
| } |
| if (!mAtmService.mSupportsPictureInPicture) { |
| return; |
| } |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| displayContent.getPinnedStackController().registerPinnedStackListener(listener); |
| } |
| } |
| |
| @Override |
| public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) { |
| try { |
| WindowState focusedWindow = getFocusedWindow(); |
| if (focusedWindow != null && focusedWindow.mClient != null) { |
| getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId); |
| } |
| } catch (RemoteException e) { |
| } |
| } |
| |
| @Override |
| public void getStableInsets(int displayId, Rect outInsets) throws RemoteException { |
| synchronized (mGlobalLock) { |
| getStableInsetsLocked(displayId, outInsets); |
| } |
| } |
| |
| void getStableInsetsLocked(int displayId, Rect outInsets) { |
| outInsets.setEmpty(); |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc != null) { |
| final DisplayInfo di = dc.getDisplayInfo(); |
| dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, |
| di.displayCutout, outInsets); |
| } |
| } |
| |
| @Override |
| public void setForwardedInsets(int displayId, Insets insets) throws RemoteException { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| return; |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final int displayOwnerUid = dc.getDisplay().getOwnerUid(); |
| if (callingUid != displayOwnerUid) { |
| throw new SecurityException( |
| "Only owner of the display can set ForwardedInsets to it."); |
| } |
| dc.setForwardedInsets(insets); |
| } |
| } |
| |
| void intersectDisplayInsetBounds(Rect display, Rect insets, Rect inOutBounds) { |
| mTmpRect3.set(display); |
| mTmpRect3.inset(insets); |
| inOutBounds.intersect(mTmpRect3); |
| } |
| |
| MousePositionTracker mMousePositionTracker = new MousePositionTracker(); |
| |
| private static class MousePositionTracker implements PointerEventListener { |
| private boolean mLatestEventWasMouse; |
| private float mLatestMouseX; |
| private float mLatestMouseY; |
| |
| void updatePosition(float x, float y) { |
| synchronized (this) { |
| mLatestEventWasMouse = true; |
| mLatestMouseX = x; |
| mLatestMouseY = y; |
| } |
| } |
| |
| @Override |
| public void onPointerEvent(MotionEvent motionEvent) { |
| if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) { |
| updatePosition(motionEvent.getRawX(), motionEvent.getRawY()); |
| } else { |
| synchronized (this) { |
| mLatestEventWasMouse = false; |
| } |
| } |
| } |
| }; |
| |
| void updatePointerIcon(IWindow client) { |
| float mouseX, mouseY; |
| |
| synchronized(mMousePositionTracker) { |
| if (!mMousePositionTracker.mLatestEventWasMouse) { |
| return; |
| } |
| mouseX = mMousePositionTracker.mLatestMouseX; |
| mouseY = mMousePositionTracker.mLatestMouseY; |
| } |
| |
| synchronized (mGlobalLock) { |
| if (mDragDropController.dragDropActiveLocked()) { |
| // Drag cursor overrides the app cursor. |
| return; |
| } |
| WindowState callingWin = windowForClientLocked(null, client, false); |
| if (callingWin == null) { |
| ProtoLog.w(WM_ERROR, "Bad requesting window %s", client); |
| return; |
| } |
| final DisplayContent displayContent = callingWin.getDisplayContent(); |
| if (displayContent == null) { |
| return; |
| } |
| WindowState windowUnderPointer = |
| displayContent.getTouchableWinAtPointLocked(mouseX, mouseY); |
| if (windowUnderPointer != callingWin) { |
| return; |
| } |
| try { |
| windowUnderPointer.mClient.updatePointerIcon( |
| windowUnderPointer.translateToWindowX(mouseX), |
| windowUnderPointer.translateToWindowY(mouseY)); |
| } catch (RemoteException e) { |
| ProtoLog.w(WM_ERROR, "unable to update pointer icon"); |
| } |
| } |
| } |
| |
| void restorePointerIconLocked(DisplayContent displayContent, float latestX, float latestY) { |
| // Mouse position tracker has not been getting updates while dragging, update it now. |
| mMousePositionTracker.updatePosition(latestX, latestY); |
| |
| WindowState windowUnderPointer = |
| displayContent.getTouchableWinAtPointLocked(latestX, latestY); |
| if (windowUnderPointer != null) { |
| try { |
| windowUnderPointer.mClient.updatePointerIcon( |
| windowUnderPointer.translateToWindowX(latestX), |
| windowUnderPointer.translateToWindowY(latestY)); |
| } catch (RemoteException e) { |
| ProtoLog.w(WM_ERROR, "unable to restore pointer icon"); |
| } |
| } else { |
| InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_DEFAULT); |
| } |
| } |
| |
| private void checkCallerOwnsDisplay(int displayId) { |
| final Display display = mDisplayManager.getDisplay(displayId); |
| if (display == null) { |
| throw new IllegalArgumentException( |
| "Cannot find display for non-existent displayId: " + displayId); |
| } |
| |
| final int callingUid = Binder.getCallingUid(); |
| final int displayOwnerUid = display.getOwnerUid(); |
| if (callingUid != displayOwnerUid) { |
| throw new SecurityException("The caller doesn't own the display."); |
| } |
| } |
| |
| /** @see Session#reparentDisplayContent(IWindow, SurfaceControl, int) */ |
| void reparentDisplayContent(IWindow client, SurfaceControl sc, int displayId) { |
| checkCallerOwnsDisplay(displayId); |
| |
| synchronized (mGlobalLock) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| final WindowState win = windowForClientLocked(null, client, false); |
| if (win == null) { |
| ProtoLog.w(WM_ERROR, "Bad requesting window %s", client); |
| return; |
| } |
| getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc); |
| // Notifies AccessibilityController to re-compute the window observer of |
| // this embedded display |
| if (mAccessibilityController != null) { |
| mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId, |
| win); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| } |
| |
| /** @see Session#updateDisplayContentLocation(IWindow, int, int, int) */ |
| void updateDisplayContentLocation(IWindow client, int x, int y, int displayId) { |
| checkCallerOwnsDisplay(displayId); |
| |
| synchronized (mGlobalLock) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| final WindowState win = windowForClientLocked(null, client, false); |
| if (win == null) { |
| ProtoLog.w(WM_ERROR, "Bad requesting window %s", client); |
| return; |
| } |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.updateLocation(win, x, y); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| } |
| |
| /** |
| * Update a tap exclude region in the window identified by the provided id. Touches down on this |
| * region will not: |
| * <ol> |
| * <li>Switch focus to this window.</li> |
| * <li>Move the display of this window to top.</li> |
| * <li>Send the touch events to this window.</li> |
| * </ol> |
| * Passing an invalid region will remove the area from the exclude region of this window. |
| */ |
| void updateTapExcludeRegion(IWindow client, Region region) { |
| synchronized (mGlobalLock) { |
| final WindowState callingWin = windowForClientLocked(null, client, false); |
| if (callingWin == null) { |
| ProtoLog.w(WM_ERROR, "Bad requesting window %s", client); |
| return; |
| } |
| callingWin.updateTapExcludeRegion(region); |
| } |
| } |
| |
| @Override |
| public void dontOverrideDisplayInfo(int displayId) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); |
| if (dc == null) { |
| throw new IllegalArgumentException( |
| "Trying to configure a non existent display."); |
| } |
| // We usually set the override info in DisplayManager so that we get consistent |
| // values when displays are changing. However, we don't do this for displays that |
| // serve as containers for ActivityViews because we don't want letter-/pillar-boxing |
| // during resize. |
| dc.mShouldOverrideDisplayConfiguration = false; |
| mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId, |
| null /* info */); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| |
| @Override |
| public int getWindowingMode(int displayId) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getWindowingMode()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to get windowing mode of a display that does not exist: %d", |
| displayId); |
| return WindowConfiguration.WINDOWING_MODE_UNDEFINED; |
| } |
| return mDisplayWindowSettings.getWindowingModeLocked(displayContent); |
| } |
| } |
| |
| @Override |
| public void setWindowingMode(int displayId, int mode) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setWindowingMode()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to set windowing mode to a display that does not exist: %d", |
| displayId); |
| return; |
| } |
| |
| int lastWindowingMode = displayContent.getWindowingMode(); |
| mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode); |
| |
| displayContent.reconfigureDisplayLocked(); |
| |
| if (lastWindowingMode != displayContent.getWindowingMode()) { |
| // reconfigure won't detect this change in isolation because the windowing mode is |
| // already set on the display, so fire off a new config now. |
| |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| // direct call since lock is shared. |
| displayContent.sendNewConfiguration(); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| // Now that all configurations are updated, execute pending transitions |
| displayContent.executeAppTransition(); |
| } |
| } |
| } |
| |
| @Override |
| public @RemoveContentMode int getRemoveContentMode(int displayId) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getRemoveContentMode()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to get remove mode of a display that does not exist: %d", |
| displayId); |
| return REMOVE_CONTENT_MODE_UNDEFINED; |
| } |
| return mDisplayWindowSettings.getRemoveContentModeLocked(displayContent); |
| } |
| } |
| |
| @Override |
| public void setRemoveContentMode(int displayId, @RemoveContentMode int mode) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setRemoveContentMode()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to set remove mode to a display that does not exist: %d", |
| displayId); |
| return; |
| } |
| |
| mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode); |
| |
| displayContent.reconfigureDisplayLocked(); |
| } |
| } |
| |
| @Override |
| public boolean shouldShowWithInsecureKeyguard(int displayId) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowWithInsecureKeyguard()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to get flag of a display that does not exist: %d", |
| displayId); |
| return false; |
| } |
| return mDisplayWindowSettings.shouldShowWithInsecureKeyguardLocked(displayContent); |
| } |
| } |
| |
| @Override |
| public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, |
| "setShouldShowWithInsecureKeyguard()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to set flag to a display that does not exist: " |
| + "%d", displayId); |
| return; |
| } |
| |
| mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent, |
| shouldShow); |
| |
| displayContent.reconfigureDisplayLocked(); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public boolean shouldShowSystemDecors(int displayId) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowSystemDecors()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to get system decors flag of a display that does " |
| + "not exist: %d", displayId); |
| return false; |
| } |
| if (displayContent.isUntrustedVirtualDisplay()) { |
| return false; |
| } |
| return displayContent.supportsSystemDecorations(); |
| } |
| } |
| |
| @Override |
| public void setShouldShowSystemDecors(int displayId, boolean shouldShow) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to set system decors flag to a display that " |
| + "does not exist: %d", displayId); |
| return; |
| } |
| if (displayContent.isUntrustedVirtualDisplay()) { |
| throw new SecurityException("Attempted to set system decors flag to an " |
| + "untrusted virtual display: " + displayId); |
| } |
| |
| mDisplayWindowSettings.setShouldShowSystemDecorsLocked(displayContent, shouldShow); |
| |
| displayContent.reconfigureDisplayLocked(); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public boolean shouldShowIme(int displayId) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowIme()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| boolean show; |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| ProtoLog.w(WM_ERROR, |
| "Attempted to get IME flag of a display that does not exist: %d", |
| displayId); |
| return false; |
| } |
| synchronized (mGlobalLock) { |
| show = dc.canShowIme(); |
| } |
| |
| return show; |
| } |
| |
| @Override |
| public void setShouldShowIme(int displayId, boolean shouldShow) { |
| if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowIme()")) { |
| throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); |
| if (displayContent == null) { |
| ProtoLog.w(WM_ERROR, "Attempted to set IME flag to a display that does not " |
| + "exist: %d", displayId); |
| return; |
| } |
| if (displayContent.isUntrustedVirtualDisplay()) { |
| throw new SecurityException("Attempted to set IME flag to an untrusted " |
| + "virtual display: " + displayId); |
| } |
| |
| mDisplayWindowSettings.setShouldShowImeLocked(displayContent, shouldShow); |
| |
| displayContent.reconfigureDisplayLocked(); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @Override |
| public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) |
| throws RemoteException { |
| if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) { |
| throw new SecurityException( |
| "Requires REGISTER_WINDOW_MANAGER_LISTENERS permission"); |
| } |
| mPolicy.registerShortcutKey(shortcutCode, shortcutKeyReceiver); |
| } |
| |
| private final class LocalService extends WindowManagerInternal { |
| |
| @Override |
| public void clearSnapshotCache() { |
| synchronized (mGlobalLock) { |
| mTaskSnapshotController.clearSnapshotCache(); |
| } |
| } |
| |
| @Override |
| public void requestTraversalFromDisplayManager() { |
| synchronized (mGlobalLock) { |
| requestTraversal(); |
| } |
| } |
| |
| @Override |
| public void setMagnificationSpec(int displayId, MagnificationSpec spec) { |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController != null) { |
| mAccessibilityController.setMagnificationSpecLocked(displayId, spec); |
| } else { |
| throw new IllegalStateException("Magnification callbacks not set!"); |
| } |
| } |
| if (Binder.getCallingPid() != myPid()) { |
| spec.recycle(); |
| } |
| } |
| |
| @Override |
| public void setForceShowMagnifiableBounds(int displayId, boolean show) { |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController != null) { |
| mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show); |
| } else { |
| throw new IllegalStateException("Magnification callbacks not set!"); |
| } |
| } |
| } |
| |
| @Override |
| public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) { |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController != null) { |
| mAccessibilityController.getMagnificationRegionLocked(displayId, |
| magnificationRegion); |
| } else { |
| throw new IllegalStateException("Magnification callbacks not set!"); |
| } |
| } |
| } |
| |
| @Override |
| public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { |
| synchronized (mGlobalLock) { |
| WindowState windowState = mWindowMap.get(windowToken); |
| if (windowState == null) { |
| return null; |
| } |
| MagnificationSpec spec = null; |
| if (mAccessibilityController != null) { |
| spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState); |
| } |
| if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { |
| return null; |
| } |
| spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec); |
| spec.scale *= windowState.mGlobalScale; |
| return spec; |
| } |
| } |
| |
| @Override |
| public boolean setMagnificationCallbacks(int displayId, |
| @Nullable MagnificationCallbacks callbacks) { |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController == null) { |
| mAccessibilityController = new AccessibilityController( |
| WindowManagerService.this); |
| } |
| boolean result = mAccessibilityController.setMagnificationCallbacksLocked( |
| displayId, callbacks); |
| if (!mAccessibilityController.hasCallbacksLocked()) { |
| mAccessibilityController = null; |
| } |
| return result; |
| } |
| } |
| |
| @Override |
| public boolean setWindowsForAccessibilityCallback(int displayId, |
| WindowsForAccessibilityCallback callback) { |
| synchronized (mGlobalLock) { |
| if (mAccessibilityController == null) { |
| mAccessibilityController = new AccessibilityController( |
| WindowManagerService.this); |
| } |
| final boolean result = |
| mAccessibilityController.setWindowsForAccessibilityCallbackLocked( |
| displayId, callback); |
| if (!mAccessibilityController.hasCallbacksLocked()) { |
| mAccessibilityController = null; |
| } |
| return result; |
| } |
| } |
| |
| @Override |
| public void setInputFilter(IInputFilter filter) { |
| mInputManager.setInputFilter(filter); |
| } |
| |
| @Override |
| public IBinder getFocusedWindowToken() { |
| synchronized (mGlobalLock) { |
| WindowState windowState = getFocusedWindowLocked(); |
| if (windowState != null) { |
| return windowState.mClient.asBinder(); |
| } |
| return null; |
| } |
| } |
| |
| @Override |
| public boolean isKeyguardLocked() { |
| return WindowManagerService.this.isKeyguardLocked(); |
| } |
| |
| @Override |
| public boolean isKeyguardShowingAndNotOccluded() { |
| return WindowManagerService.this.isKeyguardShowingAndNotOccluded(); |
| } |
| |
| @Override |
| public void showGlobalActions() { |
| WindowManagerService.this.showGlobalActions(); |
| } |
| |
| @Override |
| public void getWindowFrame(IBinder token, Rect outBounds) { |
| synchronized (mGlobalLock) { |
| WindowState windowState = mWindowMap.get(token); |
| if (windowState != null) { |
| outBounds.set(windowState.getFrameLw()); |
| } else { |
| outBounds.setEmpty(); |
| } |
| } |
| } |
| |
| @Override |
| public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) { |
| final WindowContainer container = displayId == INVALID_DISPLAY |
| ? mRoot : mRoot.getDisplayContent(displayId); |
| if (container == null) { |
| // The waiting container doesn't exist, no need to wait to run the callback. Run and |
| // return; |
| callback.run(); |
| return; |
| } |
| boolean allWindowsDrawn = false; |
| synchronized (mGlobalLock) { |
| container.waitForAllWindowsDrawn(); |
| mWindowPlacerLocked.requestTraversal(); |
| mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container); |
| if (container.mWaitingForDrawn.isEmpty()) { |
| allWindowsDrawn = true; |
| } else { |
| mWaitingForDrawnCallbacks.put(container, callback); |
| mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout); |
| checkDrawnWindowsLocked(); |
| } |
| } |
| if (allWindowsDrawn) { |
| callback.run(); |
| } |
| } |
| |
| @Override |
| public void setForcedDisplaySize(int displayId, int width, int height) { |
| WindowManagerService.this.setForcedDisplaySize(displayId, width, height); |
| } |
| |
| @Override |
| public void clearForcedDisplaySize(int displayId) { |
| WindowManagerService.this.clearForcedDisplaySize(displayId); |
| } |
| |
| @Override |
| public void addWindowToken(IBinder token, int type, int displayId) { |
| WindowManagerService.this.addWindowToken(token, type, displayId); |
| } |
| |
| @Override |
| public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) { |
| synchronized (mGlobalLock) { |
| if (removeWindows) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc == null) { |
| ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" |
| + " for non-exiting displayId=%d", binder, displayId); |
| return; |
| } |
| |
| final WindowToken token = dc.removeWindowToken(binder); |
| if (token == null) { |
| ProtoLog.w(WM_ERROR, |
| "removeWindowToken: Attempted to remove non-existing token: %s", |
| binder); |
| return; |
| } |
| |
| token.removeAllWindowsIfPossible(); |
| } |
| WindowManagerService.this.removeWindowToken(binder, displayId); |
| } |
| } |
| |
| // TODO(multi-display): currently only used by PWM to notify keyguard transitions as well |
| // forwarding it to SystemUI for synchronizing status and navigation bar animations. |
| @Override |
| public void registerAppTransitionListener(AppTransitionListener listener) { |
| synchronized (mGlobalLock) { |
| getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener); |
| } |
| } |
| |
| @Override |
| public void reportPasswordChanged(int userId) { |
| mKeyguardDisableHandler.updateKeyguardEnabled(userId); |
| } |
| |
| @Override |
| public int getInputMethodWindowVisibleHeight(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| return dc.mDisplayFrames.getInputMethodWindowVisibleHeight(); |
| } |
| } |
| |
| @Override |
| public void updateInputMethodWindowStatus(@NonNull IBinder imeToken, |
| boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) { |
| mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed); |
| } |
| |
| @Override |
| public void updateInputMethodTargetWindow(@NonNull IBinder imeToken, |
| @NonNull IBinder imeTargetWindowToken) { |
| // TODO (b/34628091): Use this method to address the window animation issue. |
| if (DEBUG_INPUT_METHOD) { |
| Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken |
| + " imeTargetWindowToken=" + imeTargetWindowToken); |
| } |
| final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); |
| if (imeTarget != null) { |
| imeTarget.getDisplayContent().updateImeControlTarget(imeTarget); |
| } |
| } |
| |
| @Override |
| public boolean isHardKeyboardAvailable() { |
| synchronized (mGlobalLock) { |
| return mHardKeyboardAvailable; |
| } |
| } |
| |
| @Override |
| public void setOnHardKeyboardStatusChangeListener( |
| OnHardKeyboardStatusChangeListener listener) { |
| synchronized (mGlobalLock) { |
| mHardKeyboardStatusChangeListener = listener; |
| } |
| } |
| |
| @Override |
| public boolean isStackVisibleLw(int windowingMode) { |
| final DisplayContent dc = getDefaultDisplayContentLocked(); |
| return dc.isStackVisible(windowingMode); |
| } |
| |
| @Override |
| public void computeWindowsForAccessibility(int displayId) { |
| final AccessibilityController accessibilityController; |
| synchronized (mGlobalLock) { |
| accessibilityController = mAccessibilityController; |
| } |
| if (accessibilityController != null) { |
| accessibilityController.performComputeChangedWindowsNotLocked(displayId, true); |
| } |
| } |
| |
| @Override |
| public void setVr2dDisplayId(int vr2dDisplayId) { |
| if (DEBUG_DISPLAY) { |
| Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId); |
| } |
| synchronized (mGlobalLock) { |
| mVr2dDisplayId = vr2dDisplayId; |
| } |
| } |
| |
| @Override |
| public void registerDragDropControllerCallback(IDragDropCallback callback) { |
| mDragDropController.registerCallback(callback); |
| } |
| |
| @Override |
| public void lockNow() { |
| WindowManagerService.this.lockNow(null); |
| } |
| |
| @Override |
| public int getWindowOwnerUserId(IBinder token) { |
| synchronized (mGlobalLock) { |
| WindowState window = mWindowMap.get(token); |
| if (window != null) { |
| return UserHandle.getUserId(window.mOwnerUid); |
| } |
| return UserHandle.USER_NULL; |
| } |
| } |
| |
| @Override |
| public boolean isUidFocused(int uid) { |
| synchronized (mGlobalLock) { |
| for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { |
| final DisplayContent displayContent = mRoot.getChildAt(i); |
| if (displayContent.mCurrentFocus != null |
| && uid == displayContent.mCurrentFocus.getOwningUid()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| @Override |
| public boolean isInputMethodClientFocus(int uid, int pid, int displayId) { |
| if (displayId == Display.INVALID_DISPLAY) { |
| return false; |
| } |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent(); |
| if (displayContent == null |
| || displayContent.getDisplayId() != displayId |
| || !displayContent.hasAccess(uid)) { |
| return false; |
| } |
| if (displayContent.isInputMethodClientFocus(uid, pid)) { |
| return true; |
| } |
| // Okay, how about this... what is the current focus? |
| // It seems in some cases we may not have moved the IM |
| // target window, such as when it was in a pop-up window, |
| // so let's also look at the current focus. (An example: |
| // go to Gmail, start searching so the keyboard goes up, |
| // press home. Sometimes the IME won't go down.) |
| // Would be nice to fix this more correctly, but it's |
| // way at the end of a release, and this should be good enough. |
| final WindowState currentFocus = displayContent.mCurrentFocus; |
| if (currentFocus != null && currentFocus.mSession.mUid == uid |
| && currentFocus.mSession.mPid == pid) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void showImePostLayout(IBinder imeTargetWindowToken) { |
| synchronized (mGlobalLock) { |
| WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); |
| if (imeTarget == null) { |
| return; |
| } |
| imeTarget = imeTarget.getImeControlTarget(); |
| |
| final int displayId = imeTarget.getDisplayId(); |
| mRoot.getDisplayContent(displayId).getInsetsStateController().getImeSourceProvider() |
| .scheduleShowImePostLayout(imeTarget); |
| } |
| } |
| |
| @Override |
| public void hideIme(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| if (dc != null) { |
| WindowState imeTarget = dc.getImeControlTarget(); |
| if (imeTarget == null) { |
| return; |
| } |
| // If there was a pending IME show(), reset it as IME has been |
| // requested to be hidden. |
| imeTarget.getDisplayContent().getInsetsStateController().getImeSourceProvider() |
| .abortShowImePostLayout(); |
| imeTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isUidAllowedOnDisplay(int displayId, int uid) { |
| if (displayId == Display.DEFAULT_DISPLAY) { |
| return true; |
| } |
| if (displayId == Display.INVALID_DISPLAY) { |
| return false; |
| } |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| return displayContent != null && displayContent.hasAccess(uid); |
| } |
| } |
| |
| @Override |
| public int getDisplayIdForWindow(IBinder windowToken) { |
| synchronized (mGlobalLock) { |
| final WindowState window = mWindowMap.get(windowToken); |
| if (window != null) { |
| return window.getDisplayContent().getDisplayId(); |
| } |
| return Display.INVALID_DISPLAY; |
| } |
| } |
| |
| @Override |
| public int getTopFocusedDisplayId() { |
| synchronized (mGlobalLock) { |
| return mRoot.getTopFocusedDisplayContent().getDisplayId(); |
| } |
| } |
| |
| @Override |
| public Context getTopFocusedDisplayUiContext() { |
| synchronized (mGlobalLock) { |
| return mRoot.getTopFocusedDisplayContent().getDisplayUiContext(); |
| } |
| } |
| |
| @Override |
| public boolean shouldShowSystemDecorOnDisplay(int displayId) { |
| synchronized (mGlobalLock) { |
| return WindowManagerService.this.shouldShowSystemDecors(displayId); |
| } |
| } |
| |
| @Override |
| public boolean shouldShowIme(int displayId) { |
| synchronized (mGlobalLock) { |
| return WindowManagerService.this.shouldShowIme(displayId); |
| } |
| } |
| |
| @Override |
| public void addNonHighRefreshRatePackage(@NonNull String packageName) { |
| synchronized (mGlobalLock) { |
| mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy() |
| .addNonHighRefreshRatePackage(packageName)); |
| } |
| } |
| |
| @Override |
| public void removeNonHighRefreshRatePackage(@NonNull String packageName) { |
| synchronized (mGlobalLock) { |
| mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy() |
| .removeNonHighRefreshRatePackage(packageName)); |
| } |
| } |
| |
| @Override |
| public boolean isTouchableDisplay(int displayId) { |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| final Configuration configuration = |
| displayContent != null ? displayContent.getConfiguration() : null; |
| return configuration != null |
| && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER; |
| } |
| } |
| |
| @Override |
| public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) { |
| return mKeyInterceptionInfoForToken.get(inputToken); |
| } |
| |
| @Override |
| public void setAccessibilityIdToSurfaceMetadata( |
| IBinder windowToken, int accessibilityWindowId) { |
| synchronized (mGlobalLock) { |
| final WindowState state = mWindowMap.get(windowToken); |
| if (state == null) { |
| Slog.w(TAG, "Cannot find window which accessibility connection is added to"); |
| return; |
| } |
| try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { |
| t.setMetadata( |
| state.mSurfaceControl, |
| SurfaceControl.METADATA_ACCESSIBILITY_ID, |
| accessibilityWindowId); |
| t.apply(); |
| } finally { |
| SurfaceControl.closeTransaction(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken, |
| int displayId) { |
| final IBinder destinationInputToken; |
| |
| synchronized (mGlobalLock) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| return false; |
| } |
| final WindowState imeWindow = displayContent.mInputMethodWindow; |
| if (imeWindow == null) { |
| return false; |
| } |
| if (imeWindow.mInputChannel == null) { |
| return false; |
| } |
| destinationInputToken = imeWindow.mInputChannel.getToken(); |
| } |
| |
| return mInputManager.transferTouchFocus(sourceInputToken, destinationInputToken); |
| } |
| } |
| |
| void registerAppFreezeListener(AppFreezeListener listener) { |
| if (!mAppFreezeListeners.contains(listener)) { |
| mAppFreezeListeners.add(listener); |
| } |
| } |
| |
| void unregisterAppFreezeListener(AppFreezeListener listener) { |
| mAppFreezeListeners.remove(listener); |
| } |
| |
| /** |
| * WARNING: This interrupts surface updates, be careful! Don't |
| * execute within the transaction for longer than you would |
| * execute on an animation thread. |
| * WARNING: This method contains locks known to the State of California |
| * to cause Deadlocks and other conditions. |
| * |
| * Begins a surface transaction with which the AM can batch operations. |
| * All Surface updates performed by the WindowManager following this |
| * will not appear on screen until after the call to |
| * closeSurfaceTransaction. |
| * |
| * ActivityManager can use this to ensure multiple 'commands' will all |
| * be reflected in a single frame. For example when reparenting a window |
| * which was previously hidden due to it's parent properties, we may |
| * need to ensure it is hidden in the same frame that the properties |
| * from the new parent are inherited, otherwise it could be revealed |
| * mistakenly. |
| * |
| * TODO(b/36393204): We can investigate totally replacing #deferSurfaceLayout |
| * with something like this but it seems that some existing cases of |
| * deferSurfaceLayout may be a little too broad, in particular the total |
| * enclosure of startActivityUnchecked which could run for quite some time. |
| */ |
| void inSurfaceTransaction(Runnable exec) { |
| SurfaceControl.openTransaction(); |
| try { |
| exec.run(); |
| } finally { |
| SurfaceControl.closeTransaction(); |
| } |
| } |
| |
| /** Called to inform window manager if non-Vr UI shoul be disabled or not. */ |
| public void disableNonVrUi(boolean disable) { |
| synchronized (mGlobalLock) { |
| // Allow alert window notifications to be shown if non-vr UI is enabled. |
| final boolean showAlertWindowNotifications = !disable; |
| if (showAlertWindowNotifications == mShowAlertWindowNotifications) { |
| return; |
| } |
| mShowAlertWindowNotifications = showAlertWindowNotifications; |
| |
| for (int i = mSessions.size() - 1; i >= 0; --i) { |
| final Session s = mSessions.valueAt(i); |
| s.setShowingAlertWindowNotificationAllowed(mShowAlertWindowNotifications); |
| } |
| } |
| } |
| |
| boolean hasWideColorGamutSupport() { |
| return mHasWideColorGamutSupport && |
| SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1; |
| } |
| |
| boolean hasHdrSupport() { |
| return mHasHdrSupport && hasWideColorGamutSupport(); |
| } |
| |
| void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) { |
| if (!win.hideNonSystemOverlayWindowsWhenVisible() |
| && !mHidingNonSystemOverlayWindows.contains(win)) { |
| return; |
| } |
| final boolean systemAlertWindowsHidden = !mHidingNonSystemOverlayWindows.isEmpty(); |
| if (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible()) { |
| if (!mHidingNonSystemOverlayWindows.contains(win)) { |
| mHidingNonSystemOverlayWindows.add(win); |
| } |
| } else { |
| mHidingNonSystemOverlayWindows.remove(win); |
| } |
| |
| final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); |
| |
| if (systemAlertWindowsHidden == hideSystemAlertWindows) { |
| return; |
| } |
| |
| mRoot.forAllWindows((w) -> { |
| w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); |
| }, false /* traverseTopToBottom */); |
| } |
| |
| /** Called from Accessibility Controller to apply magnification spec */ |
| public void applyMagnificationSpecLocked(int displayId, MagnificationSpec spec) { |
| final DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent != null) { |
| displayContent.applyMagnificationSpec(spec); |
| } |
| } |
| |
| SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) { |
| return mSurfaceControlFactory.apply(s); |
| } |
| |
| /** |
| * Called when the state of lock task mode changes. This should be used to disable immersive |
| * mode confirmation. |
| * |
| * @param lockTaskState the new lock task mode state. One of |
| * {@link ActivityManager#LOCK_TASK_MODE_NONE}, |
| * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, |
| * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. |
| */ |
| void onLockTaskStateChanged(int lockTaskState) { |
| // TODO: pass in displayId to determine which display the lock task state changed |
| synchronized (mGlobalLock) { |
| final PooledConsumer c = PooledLambda.obtainConsumer( |
| DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState); |
| mRoot.forAllDisplayPolicies(c); |
| c.recycle(); |
| } |
| } |
| |
| /** |
| * Updates {@link WindowManagerPolicy} with new value about whether AOD is showing. If AOD |
| * has changed, this will trigger a {@link WindowSurfacePlacer#performSurfacePlacement} to |
| * ensure the new value takes effect. |
| */ |
| public void setAodShowing(boolean aodShowing) { |
| synchronized (mGlobalLock) { |
| if (mPolicy.setAodShowing(aodShowing)) { |
| mWindowPlacerLocked.performSurfacePlacement(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) { |
| boolean isDown; |
| boolean isUp; |
| |
| if (ev instanceof KeyEvent) { |
| KeyEvent keyEvent = (KeyEvent) ev; |
| isDown = keyEvent.getAction() == KeyEvent.ACTION_DOWN; |
| isUp = keyEvent.getAction() == KeyEvent.ACTION_UP; |
| } else { |
| MotionEvent motionEvent = (MotionEvent) ev; |
| isDown = motionEvent.getAction() == MotionEvent.ACTION_DOWN; |
| isUp = motionEvent.getAction() == MotionEvent.ACTION_UP; |
| } |
| final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE; |
| |
| // For ACTION_DOWN, syncInputTransactions before injecting input. |
| // For all mouse events, also sync before injecting. |
| // For ACTION_UP, sync after injecting. |
| if (isDown || isMouseEvent) { |
| syncInputTransactions(); |
| } |
| final boolean result = |
| LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode); |
| if (isUp) { |
| syncInputTransactions(); |
| } |
| return result; |
| } |
| |
| @Override |
| public void syncInputTransactions() { |
| waitForAnimationsToComplete(); |
| |
| synchronized (mGlobalLock) { |
| mWindowPlacerLocked.performSurfacePlacementIfScheduled(); |
| mRoot.forAllDisplays(displayContent -> |
| displayContent.getInputMonitor().updateInputWindowsImmediately()); |
| } |
| |
| mTransactionFactory.get().syncInputWindows().apply(true); |
| } |
| |
| private void waitForAnimationsToComplete() { |
| synchronized (mGlobalLock) { |
| long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; |
| while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) { |
| long startTime = System.currentTimeMillis(); |
| try { |
| mGlobalLock.wait(timeoutRemaining); |
| } catch (InterruptedException e) { |
| } |
| timeoutRemaining -= (System.currentTimeMillis() - startTime); |
| } |
| |
| if (mRoot.isAnimating(TRANSITION | CHILDREN)) { |
| Log.w(TAG, "Timed out waiting for animations to complete."); |
| } |
| } |
| } |
| |
| void onAnimationFinished() { |
| synchronized (mGlobalLock) { |
| mGlobalLock.notifyAll(); |
| } |
| } |
| |
| private void onPointerDownOutsideFocusLocked(IBinder touchedToken) { |
| WindowState touchedWindow = mInputToWindowMap.get(touchedToken); |
| if (touchedWindow == null) { |
| // if a user taps outside the currently focused window onto an embedded window, treat |
| // it as if the host window was tapped. |
| touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken); |
| } |
| |
| if (touchedWindow == null || !touchedWindow.canReceiveKeys(true /* fromUserTouch */)) { |
| // If the window that received the input event cannot receive keys, don't move the |
| // display it's on to the top since that window won't be able to get focus anyway. |
| return; |
| } |
| |
| final DisplayContent displayContent = touchedWindow.getDisplayContent(); |
| if (!displayContent.isOnTop()) { |
| displayContent.positionDisplayAt(WindowContainer.POSITION_TOP, |
| true /* includingParents */); |
| } |
| handleTaskFocusChange(touchedWindow.getTask()); |
| } |
| |
| private void handleTaskFocusChange(Task task) { |
| if (task == null) { |
| return; |
| } |
| |
| final ActivityStack stack = task.getStack(); |
| // We ignore home stack since we don't want home stack to move to front when touched. |
| // Specifically, in freeform we don't want tapping on home to cause the freeform apps to go |
| // behind home. See b/117376413 |
| if (stack.isActivityTypeHome()) { |
| return; |
| } |
| |
| try { |
| mActivityTaskManager.setFocusedTask(task.mTaskId); |
| } catch (RemoteException e) { |
| } |
| } |
| |
| /** |
| * Assigns an InputChannel to a SurfaceControl and configures it to receive |
| * touch input according to it's on-screen geometry. |
| * |
| * Used by WindowlessWindowManager to enable input on SurfaceControl embedded |
| * views. |
| */ |
| void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface, |
| IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) { |
| final InputApplicationHandle applicationHandle; |
| final String name; |
| final InputChannel[] inputChannels; |
| final InputChannel clientChannel; |
| final InputChannel serverChannel; |
| synchronized (mGlobalLock) { |
| EmbeddedWindowController.EmbeddedWindow win = |
| new EmbeddedWindowController.EmbeddedWindow(window, |
| mInputToWindowMap.get(hostInputToken), callingUid, callingPid); |
| name = win.getName(); |
| |
| inputChannels = InputChannel.openInputChannelPair(name); |
| serverChannel = inputChannels[0]; |
| clientChannel = inputChannels[1]; |
| mInputManager.registerInputChannel(serverChannel); |
| mEmbeddedWindowController.add(clientChannel.getToken(), win); |
| if (serverChannel.getToken() != clientChannel.getToken()) { |
| throw new IllegalStateException("Client and Server channel are expected to" |
| + "be the same"); |
| } |
| |
| applicationHandle = win.getApplicationHandle(); |
| } |
| |
| updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, |
| name, applicationHandle, flags); |
| |
| clientChannel.transferTo(outInputChannel); |
| clientChannel.dispose(); |
| // Prevent the java finalizer from breaking the input channel. But we won't |
| // do any further management so we just release the java ref and let the |
| // InputDispatcher hold the last ref. |
| serverChannel.release(); |
| } |
| |
| private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, |
| int displayId, SurfaceControl surface, String name, |
| InputApplicationHandle applicationHandle, int flags) { |
| InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); |
| h.token = channelToken; |
| h.name = name; |
| |
| final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE |
| | LayoutParams.FLAG_SLIPPERY); |
| h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; |
| h.layoutParamsType = 0; |
| h.dispatchingTimeoutNanos = -1; |
| h.canReceiveKeys = false; |
| h.hasFocus = false; |
| h.hasWallpaper = false; |
| h.paused = false; |
| |
| h.ownerUid = callingUid; |
| h.ownerPid = callingPid; |
| |
| h.inputFeatures = 0; |
| |
| h.replaceTouchableRegionWithCrop(null); |
| |
| SurfaceControl.Transaction t = mTransactionFactory.get(); |
| t.setInputWindowInfo(surface, h); |
| t.apply(); |
| t.close(); |
| } |
| |
| /** |
| * Updates the flags on an existing surface's input channel. This assumes the surface provided |
| * is the one associated with the provided input-channel. If this isn't the case, behavior |
| * is undefined. |
| */ |
| void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, |
| int flags) { |
| final InputApplicationHandle applicationHandle; |
| final String name; |
| final EmbeddedWindowController.EmbeddedWindow win; |
| synchronized (mGlobalLock) { |
| win = mEmbeddedWindowController.get(channelToken); |
| if (win == null) { |
| Slog.e(TAG, "Couldn't find window for provided channelToken."); |
| return; |
| } |
| name = win.getName(); |
| applicationHandle = win.getApplicationHandle(); |
| } |
| |
| updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, |
| applicationHandle, flags); |
| } |
| |
| /** Return whether layer tracing is enabled */ |
| public boolean isLayerTracing() { |
| mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, |
| "isLayerTracing"); |
| long token = Binder.clearCallingIdentity(); |
| try { |
| Parcel data = null; |
| Parcel reply = null; |
| try { |
| IBinder sf = ServiceManager.getService("SurfaceFlinger"); |
| if (sf != null) { |
| reply = Parcel.obtain(); |
| data = Parcel.obtain(); |
| data.writeInterfaceToken("android.ui.ISurfaceComposer"); |
| sf.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, data, reply, 0 /* flags */); |
| return reply.readBoolean(); |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Failed to get layer tracing"); |
| } finally { |
| if (data != null) { |
| data.recycle(); |
| } |
| if (reply != null) { |
| reply.recycle(); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| return false; |
| } |
| |
| /** Enable or disable layer tracing */ |
| public void setLayerTracing(boolean enabled) { |
| mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, |
| "setLayerTracing"); |
| long token = Binder.clearCallingIdentity(); |
| try { |
| Parcel data = null; |
| try { |
| IBinder sf = ServiceManager.getService("SurfaceFlinger"); |
| if (sf != null) { |
| data = Parcel.obtain(); |
| data.writeInterfaceToken("android.ui.ISurfaceComposer"); |
| data.writeInt(enabled ? 1 : 0); |
| sf.transact(/* LAYER_TRACE_CONTROL_CODE */ 1025, data, null, 0 /* flags */); |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Failed to set layer tracing"); |
| } finally { |
| if (data != null) { |
| data.recycle(); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| |
| @Override |
| public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) { |
| if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) { |
| throw new SecurityException("Requires READ_FRAME_BUFFER permission"); |
| } |
| |
| final SurfaceControl displaySc; |
| synchronized (mGlobalLock) { |
| DisplayContent displayContent = mRoot.getDisplayContent(displayId); |
| if (displayContent == null) { |
| Slog.e(TAG, "Invalid displayId " + displayId + " for mirrorDisplay"); |
| return false; |
| } |
| |
| displaySc = displayContent.getWindowingLayer(); |
| } |
| |
| final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc); |
| outSurfaceControl.copyFrom(mirror); |
| |
| return true; |
| } |
| |
| @Override |
| public void getWindowInsets(WindowManager.LayoutParams attrs, |
| int displayId, Rect outContentInsets, Rect outStableInsets, |
| DisplayCutout.ParcelableWrapper displayCutout) { |
| synchronized (mGlobalLock) { |
| final DisplayContent dc = mRoot.getDisplayContent(displayId); |
| final WindowToken windowToken = dc.getWindowToken(attrs.token); |
| final ActivityRecord activity; |
| if (windowToken != null && windowToken.asActivityRecord() != null) { |
| activity = windowToken.asActivityRecord(); |
| } else { |
| activity = null; |
| } |
| final Rect taskBounds = new Rect(); |
| final boolean floatingStack; |
| if (activity != null && activity.getTask() != null) { |
| final Task task = activity.getTask(); |
| task.getBounds(taskBounds); |
| floatingStack = task.isFloating(); |
| } else { |
| floatingStack = false; |
| } |
| final DisplayFrames displayFrames = dc.mDisplayFrames; |
| final DisplayPolicy policy = dc.getDisplayPolicy(); |
| policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack, |
| new Rect(), outContentInsets, outStableInsets, displayCutout); |
| } |
| } |
| } |