/*
|
* 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_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.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.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE;
|
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
|
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
|
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_DRAG;
|
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_PRESENTATION;
|
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
|
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
|
import static android.view.WindowManager.LayoutParams.TYPE_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.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.ActivityStackSupervisor.PRESERVE_WINDOWS;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
|
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_TRANSACTIONS;
|
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
|
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_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.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.database.ContentObserver;
|
import android.graphics.Bitmap;
|
import android.graphics.Insets;
|
import android.graphics.Matrix;
|
import android.graphics.PixelFormat;
|
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.hardware.power.V1_0.PowerHint;
|
|
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.DeviceConfig;
|
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.EventLog;
|
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.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.InputChannel;
|
import android.view.InputDevice;
|
import android.view.InputEvent;
|
import android.view.InputEventReceiver;
|
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.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.util.DumpUtils;
|
import com.android.internal.util.FastPrintWriter;
|
import com.android.internal.util.LatencyTracker;
|
import com.android.internal.util.Preconditions;
|
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.EventLogTags;
|
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.utils.PriorityDump;
|
|
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.Date;
|
import java.util.List;
|
|
|
|
/** {@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;
|
static final boolean localLOGV = DEBUG;
|
|
/** 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;
|
|
/**
|
* Dim surface layer is immediately below target window.
|
*/
|
static final int LAYER_OFFSET_DIM = 1;
|
|
/**
|
* 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
|
/**
|
* If true, the window manager will do its own custom freezing and general
|
* management of the screen during rotation.
|
*/
|
static final boolean CUSTOM_SCREEN_ROTATION = true;
|
|
// 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 int MAX_SCREENSHOT_RETRIES = 3;
|
|
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;
|
|
// 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;
|
|
private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
|
|
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;
|
|
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
|
@Override
|
public void onVrStateChanged(boolean enabled) {
|
synchronized (mGlobalLock) {
|
mVrModeEnabled = enabled;
|
mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
|
DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled));
|
}
|
}
|
};
|
|
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;
|
|
// TODO(b/122671846) Remove the flag below in favor of isLowRam once feature is stable
|
/**
|
* Use very low resolution task snapshots. Replaces task snapshot starting windows with
|
* splashscreen starting windows. Used on low RAM devices to save memory.
|
*/
|
final boolean mLowRamTaskSnapshotsAndRecents;
|
|
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 WindowHashMap mWindowMap = new WindowHashMap();
|
|
/** 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<AppWindowToken> 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<>();
|
|
/**
|
* Windows that clients are waiting to have drawn.
|
*/
|
ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
|
/**
|
* And the callback to make when they've all been drawn.
|
*/
|
Runnable mWaitingForDrawnCallback;
|
|
/** 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;
|
CircularDisplayMask mCircularDisplayMask;
|
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 mForceResizableTasks;
|
boolean mSupportsPictureInPicture;
|
boolean mSupportsFreeformWindowManagement;
|
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;
|
|
int getDragLayerLocked() {
|
return mPolicy.getWindowLayerFromTypeLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
|
}
|
|
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();
|
|
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;
|
|
/**
|
* A count of the windows which are 'seamlessly rotated', e.g. a surface
|
* at an old orientation is being transformed. We freeze orientation updates
|
* while any windows are seamlessly rotated, so we need to track when this
|
* hits zero so we can apply deferred orientation updates.
|
*/
|
private int mSeamlessRotationCount = 0;
|
/**
|
* True in the interval from starting seamless rotation until the last rotated
|
* window draws in the new orientation.
|
*/
|
private boolean mRotatingSeamlessly = false;
|
|
private 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);
|
|
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);
|
}
|
|
@Override
|
public void onChange(boolean selfChange, Uri uri) {
|
if (uri == null) {
|
return;
|
}
|
|
if (mImmersiveModeConfirmationsUri.equals(uri) || mPolicyControlUri.equals(uri)) {
|
updateSystemUiSettings();
|
return;
|
}
|
|
if (mDisplayInversionEnabledUri.equals(uri)) {
|
updateCircularDisplayMaskIfNeeded();
|
return;
|
}
|
|
if (mPointerLocationUri.equals(uri)) {
|
updatePointerLocation();
|
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) {
|
mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
|
DisplayPolicy::setPointerLocationEnabled, PooledLambda.__(),
|
mPointerLocationEnabled));
|
}
|
}
|
}
|
|
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 AppWindowTokens 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;
|
final 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).
|
*/
|
boolean mInTouchMode;
|
|
private ViewServer mViewServer;
|
final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
|
boolean mWindowsChanged = false;
|
|
int mSystemGestureExclusionLimitDp;
|
boolean mSystemGestureExcludedByPreQStickyImmersive;
|
|
public interface WindowChangeListener {
|
public void windowsChanged();
|
public void focusChanged();
|
}
|
|
final Configuration mTempConfiguration = new Configuration();
|
|
final HighRefreshRateBlacklist mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create();
|
|
// 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();
|
|
SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new;
|
TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new;
|
SurfaceFactory mSurfaceFactory = Surface::new;
|
|
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 AppWindowToken atoken = mRoot.getAppWindowToken(token);
|
if (atoken == null) {
|
return;
|
}
|
if (atoken.mLaunchTaskBehind) {
|
try {
|
mActivityTaskManager.notifyLaunchTaskBehindComplete(atoken.token);
|
} catch (RemoteException e) {
|
}
|
atoken.mLaunchTaskBehind = false;
|
} else {
|
atoken.updateReportedVisibilityLocked();
|
if (atoken.mEnteringAnimation) {
|
if (getRecentsAnimationController() != null
|
&& getRecentsAnimationController().isTargetApp(atoken)) {
|
// Currently running a recents animation, this will get called early because
|
// we show the recents animation target activity immediately when the
|
// animation starts. In this case, we should defer sending the finished
|
// callback until the animation successfully finishes
|
return;
|
} else {
|
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);
|
}
|
|
/**
|
* Creates and returns an instance of the WindowManagerService. This call allows the caller
|
* to override the {@link TransactionFactory} to stub functionality under test.
|
*/
|
@VisibleForTesting
|
public static WindowManagerService main(final Context context, final InputManagerService im,
|
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
|
ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
|
DisplayThread.getHandler().runWithScissors(() ->
|
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
|
atm, transactionFactory), 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, TransactionFactory transactionFactory) {
|
installLock(this, INDEX_WINDOW);
|
mGlobalLock = atm.getGlobalLock();
|
mAtmService = atm;
|
mContext = context;
|
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);
|
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);
|
mLowRamTaskSnapshotsAndRecents = context.getResources().getBoolean(
|
com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
|
mInputManager = inputManager; // Must be before createDisplayContentLocked.
|
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
|
mDisplayWindowSettings = new DisplayWindowSettings(this);
|
|
mTransactionFactory = transactionFactory;
|
mTransaction = mTransactionFactory.make();
|
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);
|
|
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(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());
|
|
mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
|
DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
|
mSystemGestureExcludedByPreQStickyImmersive =
|
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
|
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
new HandlerExecutor(mH), properties -> {
|
synchronized (mGlobalLock) {
|
final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
|
properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
|
final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
|
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
|
if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
|
|| mSystemGestureExclusionLimitDp != exclusionLimitDp) {
|
mSystemGestureExclusionLimitDp = exclusionLimitDp;
|
mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
|
mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
|
}
|
}
|
});
|
|
LocalServices.addService(WindowManagerInternal.class, new LocalService());
|
}
|
|
/**
|
* 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);
|
|
openSurfaceTransaction();
|
try {
|
createWatermarkInTransaction();
|
} finally {
|
closeSurfaceTransaction("createWatermarkInTransaction");
|
}
|
|
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)) {
|
Slog.wtf(TAG_WM, "Window Manager Crash", e);
|
}
|
throw e;
|
}
|
}
|
|
static boolean excludeWindowTypeFromTapOutTask(int windowType) {
|
switch (windowType) {
|
case TYPE_STATUS_BAR:
|
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, Rect outOutsets,
|
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;
|
}
|
|
boolean reportNewConfig = false;
|
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) {
|
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
|
+ displayId + ". Aborting.");
|
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
|
}
|
if (!displayContent.hasAccess(session.mUid)) {
|
Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
|
+ "does not have access: " + displayId + ". Aborting.");
|
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
|
}
|
|
if (mWindowMap.containsKey(client.asBinder())) {
|
Slog.w(TAG_WM, "Window " + client + " is already added");
|
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
|
}
|
|
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
|
parentWindow = windowForClientLocked(null, attrs.token, false);
|
if (parentWindow == null) {
|
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
|
}
|
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
|
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
|
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
|
}
|
}
|
|
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
|
Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
|
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
|
}
|
|
if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {
|
Slog.w(TAG_WM,
|
"Attempted to add presentation window to a non-suitable display. "
|
+ "Aborting.");
|
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
|
}
|
|
AppWindowToken atoken = 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 (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
|
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (rootType == TYPE_INPUT_METHOD) {
|
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (rootType == TYPE_VOICE_INTERACTION) {
|
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (rootType == TYPE_WALLPAPER) {
|
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (rootType == TYPE_DREAM) {
|
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (rootType == TYPE_QS_DIALOG) {
|
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
|
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
if (type == TYPE_TOAST) {
|
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
|
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
|
parentWindow)) {
|
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
|
+ attrs.token + ". Aborting.");
|
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) {
|
atoken = token.asAppWindowToken();
|
if (atoken == null) {
|
Slog.w(TAG_WM, "Attempted to add window with non-application token "
|
+ token + ". Aborting.");
|
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
|
} else if (atoken.removed) {
|
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
|
+ token + ". Aborting.");
|
return WindowManagerGlobal.ADD_APP_EXITING;
|
} else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
|
Slog.w(TAG_WM, "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) {
|
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
} else if (rootType == TYPE_VOICE_INTERACTION) {
|
if (token.windowType != TYPE_VOICE_INTERACTION) {
|
Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
} else if (rootType == TYPE_WALLPAPER) {
|
if (token.windowType != TYPE_WALLPAPER) {
|
Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
} else if (rootType == TYPE_DREAM) {
|
if (token.windowType != TYPE_DREAM) {
|
Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
|
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
|
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
|
+ attrs.token + ". Aborting.");
|
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) {
|
Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
} else if (type == TYPE_QS_DIALOG) {
|
if (token.windowType != TYPE_QS_DIALOG) {
|
Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
|
+ attrs.token + ". Aborting.");
|
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
}
|
} else if (token.asAppWindowToken() != null) {
|
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + 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.
|
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
|
+ " that is dead, aborting.");
|
return WindowManagerGlobal.ADD_APP_EXITING;
|
}
|
|
if (win.getDisplayContent() == null) {
|
Slog.w(TAG_WM, "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());
|
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
|
|
res = displayPolicy.prepareAddWindowLw(win, 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)) {
|
Slog.w(TAG_WM, "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 AppWindowToken aToken = token.asAppWindowToken();
|
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
|
aToken.startingWindow = win;
|
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
|
+ " startingWindow=" + win);
|
}
|
|
boolean imMayMove = true;
|
|
win.mToken.addWindow(win);
|
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 (atoken != null && atoken.isVisible()
|
&& !prepareWindowReplacementTransition(atoken)) {
|
// 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(atoken);
|
}
|
|
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 (atoken != null && atoken.getTask() != null) {
|
taskBounds = mTmpRect;
|
atoken.getTask().getBounds(mTmpRect);
|
floatingStack = atoken.getTask().isFloating();
|
} else {
|
taskBounds = null;
|
floatingStack = false;
|
}
|
if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
|
outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
|
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
|
}
|
outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
|
|
if (mInTouchMode) {
|
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
|
}
|
if (win.mAppToken == null || !win.mAppToken.isClientHidden()) {
|
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*/);
|
|
if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
|
+ client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
|
|
if (win.isVisibleOrAdding() && displayContent.updateOrientationFromAppTokens()) {
|
reportNewConfig = true;
|
}
|
}
|
|
if (reportNewConfig) {
|
sendNewConfiguration(displayId);
|
}
|
|
Binder.restoreCallingIdentity(origId);
|
|
return res;
|
}
|
|
/**
|
* 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();
|
}
|
}
|
|
DisplayContent displayContent = mRoot.getDisplayContent(displayId);
|
|
// Create an instance if possible instead of waiting for the ActivityManagerService to drive
|
// the creation.
|
if (displayContent == null) {
|
final Display display = mDisplayManager.getDisplay(displayId);
|
|
if (display != null) {
|
displayContent = mRoot.createDisplayContent(display, null /* controller */);
|
}
|
}
|
|
return displayContent;
|
}
|
|
private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
|
WindowState attachedWindow) {
|
// Try using the target SDK of the root window
|
if (attachedWindow != null) {
|
return attachedWindow.mAppToken != null
|
&& attachedWindow.mAppToken.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(AppWindowToken atoken) {
|
atoken.clearAllDrawn();
|
final WindowState replacedWindow = atoken.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 = atoken.getDisplayContent();
|
dc.mOpeningApps.add(atoken);
|
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(AppWindowToken atoken) {
|
// 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 = atoken.getDisplayContent();
|
if (mDisplayFrozen && !dc.mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
|
dc.mOpeningApps.add(atoken);
|
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) {
|
return;
|
}
|
win.removeIfPossible();
|
}
|
}
|
|
/**
|
* 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) {
|
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
|
mWindowMap.remove(win.mClient.asBinder());
|
|
markForSeamlessRotation(win, false);
|
|
win.resetAppOpsState();
|
|
final DisplayContent dc = win.getDisplayContent();
|
if (dc.mCurrentFocus == null) {
|
dc.mWinRemovedSinceNullFocus.add(win);
|
}
|
mPendingRemove.remove(win);
|
mResizingWindows.remove(win);
|
updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
|
mWindowsChanged = true;
|
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Final remove of window: " + win);
|
|
final DisplayContent displayContent = win.getDisplayContent();
|
if (displayContent.mInputMethodWindow == win) {
|
displayContent.setInputMethodWindowLocked(null);
|
}
|
|
final WindowToken token = win.mToken;
|
final AppWindowToken atoken = win.mAppToken;
|
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing " + win + " from " + 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 (atoken != null) {
|
// TODO: Should this be moved into AppWindowToken.removeWindow? Might go away after
|
// re-factor.
|
atoken.firstWindowDrawn = false;
|
atoken.clearAllDrawn();
|
final TaskStack stack = atoken.getStack();
|
if (stack != null) {
|
stack.mExitingAppTokens.remove(atoken);
|
}
|
}
|
}
|
|
if (atoken != null) {
|
atoken.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.mAppToken != null) {
|
win.mAppToken.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 logSurface(SurfaceControl s, String title, String msg) {
|
String str = " SURFACE " + s + ": " + msg + " / " + title;
|
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);
|
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
|
"transparentRegionHint=" + region, false);
|
|
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
|
&& (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY
|
|| w.getDisplayContent().getParentWindow() != null)) {
|
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
|
}
|
}
|
}
|
} 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 outOverscanInsets, Rect outContentInsets,
|
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
|
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
|
SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
|
int result = 0;
|
boolean configChanged;
|
final int pid = Binder.getCallingPid();
|
final int uid = Binder.getCallingUid();
|
long origId = Binder.clearCallingIdentity();
|
final int displayId;
|
synchronized (mGlobalLock) {
|
final WindowState win = windowForClientLocked(session, client, false);
|
if (win == null) {
|
return 0;
|
}
|
displayId = win.getDisplayId();
|
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;
|
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;
|
attrChanges = win.mAttrs.copyFrom(attrs);
|
if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
|
| WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
|
win.mLayoutNeeded = true;
|
}
|
if (win.mAppToken != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
|
win.mAppToken.checkKeyguardFlagsChanged();
|
}
|
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
|
&& (mAccessibilityController != null)
|
&& (win.getDisplayId() == DEFAULT_DISPLAY
|
|| win.getDisplayContent().getParentWindow() != null)) {
|
// No move or resize, but the controller checks for title changes as well
|
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
|
}
|
|
if ((flagChanges & 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;
|
if (DEBUG_SCREEN_ON) {
|
RuntimeException stack = new RuntimeException();
|
stack.fillInStackTrace();
|
Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
|
+ " newVis=" + viewVisibility, stack);
|
}
|
|
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.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|
|| !win.mAppToken.isClientHidden());
|
|
// 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, result, win, winAnimator);
|
} catch (Exception e) {
|
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
|
|
Slog.w(TAG_WM, "Exception thrown when creating surface for client "
|
+ 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);
|
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.mAppToken != null) {
|
displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
|
}
|
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
|
"relayoutWindow: updateOrientationFromAppTokens");
|
configChanged = displayContent.updateOrientationFromAppTokens();
|
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.mAppToken != null) {
|
win.mAppToken.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(outOverscanInsets, outContentInsets, outVisibleInsets,
|
outStableInsets, outOutsets);
|
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
|
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
|
outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
|
if (localLOGV) Slog.v(
|
TAG_WM, "Relayout given client " + client.asBinder()
|
+ ", requestedWidth=" + requestedWidth
|
+ ", requestedHeight=" + requestedHeight
|
+ ", viewVisibility=" + viewVisibility
|
+ "\nRelayout returning frame=" + outFrame
|
+ ", surface=" + outSurfaceControl);
|
|
if (localLOGV || DEBUG_FOCUS) Slog.v(
|
TAG_WM, "Relayout of " + win + ": focusMayChange=" + 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: sendNewConfiguration");
|
sendNewConfiguration(displayId);
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
}
|
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()) {
|
// 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.mAppToken != null ? win.mAppToken.mAppStopped : true;
|
// We set mDestroying=true so AppWindowToken#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, 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);
|
if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurfaceControl + ": copied");
|
} else {
|
// For some reason there isn't a surface. Clear the
|
// caller's object so they see the same state.
|
Slog.w(TAG_WM, "Failed to create surface control for " + 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) {
|
final long origId = Binder.clearCallingIdentity();
|
try {
|
synchronized (mGlobalLock) {
|
WindowState win = windowForClientLocked(session, client, false);
|
if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "finishDrawingWindow: " + win + " mDrawState="
|
+ (win != null ? win.mWinAnimator.drawStateToString() : "null"));
|
if (win != null && win.mWinAnimator.finishDrawingLocked()) {
|
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;
|
}
|
final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
|
+ ", uid=" + Binder.getCallingUid() + " requires " + permission;
|
Slog.w(TAG_WM, msg);
|
return false;
|
}
|
|
@Override
|
public void addWindowToken(IBinder binder, int type, int displayId) {
|
if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
|
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
|
}
|
|
synchronized (mGlobalLock) {
|
final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
|
if (dc == null) {
|
Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
|
+ " for non-exiting displayId=" + displayId);
|
return;
|
}
|
|
WindowToken token = dc.getWindowToken(binder);
|
if (token != null) {
|
Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
|
+ " for already created window token: " + token
|
+ " displayId=" + displayId);
|
return;
|
}
|
if (type == TYPE_WALLPAPER) {
|
new WallpaperWindowToken(this, binder, true, dc,
|
true /* ownerCanManageAppTokens */);
|
} else {
|
new WindowToken(this, binder, type, true, dc, true /* ownerCanManageAppTokens */);
|
}
|
}
|
}
|
|
@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) {
|
Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
|
+ " for non-exiting displayId=" + displayId);
|
return;
|
}
|
|
final WindowToken token = dc.removeWindowToken(binder);
|
if (token == null) {
|
Slog.w(TAG_WM,
|
"removeWindowToken: Attempted to remove non-existing token: " + 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();
|
}
|
|
public void initializeRecentsAnimation(int targetActivityType,
|
IRecentsAnimationRunner recentsAnimationRunner,
|
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
|
SparseBooleanArray recentTaskIds) {
|
synchronized (mGlobalLock) {
|
mRecentsAnimationController = new RecentsAnimationController(this,
|
recentsAnimationRunner, callbacks, displayId);
|
mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
|
mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
|
}
|
}
|
|
@VisibleForTesting
|
void setRecentsAnimationController(RecentsAnimationController controller) {
|
mRecentsAnimationController = controller;
|
}
|
|
public RecentsAnimationController getRecentsAnimationController() {
|
return mRecentsAnimationController;
|
}
|
|
/**
|
* @return Whether the next recents animation can continue to start. Called from
|
* {@link RecentsAnimation#startRecentsActivity}.
|
*/
|
public boolean canStartRecentsAnimation() {
|
synchronized (mGlobalLock) {
|
// TODO(multi-display): currently only default display support recent activity
|
if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
|
return false;
|
}
|
return true;
|
}
|
}
|
|
/**
|
* Cancels any running recents animation. The caller should NOT hold the WM lock while calling
|
* this method, as it will call back into AM and may cause a deadlock. Any locking will be done
|
* in the animation controller itself.
|
*/
|
public void cancelRecentsAnimationSynchronously(
|
@RecentsAnimationController.ReorderMode int reorderMode, String reason) {
|
if (mRecentsAnimationController != null) {
|
// This call will call through to cleanupAnimation() below after the animation is
|
// canceled
|
mRecentsAnimationController.cancelAnimationSynchronously(reorderMode, reason);
|
}
|
}
|
|
public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
|
synchronized (mGlobalLock) {
|
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();
|
}
|
}
|
}
|
|
public void setAppFullscreen(IBinder token, boolean toOpaque) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken atoken = mRoot.getAppWindowToken(token);
|
if (atoken != null) {
|
atoken.setFillsParent(toOpaque);
|
setWindowOpaqueLocked(token, toOpaque);
|
mWindowPlacerLocked.requestTraversal();
|
}
|
}
|
}
|
|
public void setWindowOpaque(IBinder token, boolean isOpaque) {
|
synchronized (mGlobalLock) {
|
setWindowOpaqueLocked(token, isOpaque);
|
}
|
}
|
|
private void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
|
final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
|
if (wtoken != null) {
|
final WindowState win = wtoken.findMainWindow();
|
if (win == null) {
|
return;
|
}
|
isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
|
win.mWinAnimator.setOpaqueLocked(isOpaque);
|
}
|
}
|
|
public void setDockedStackCreateState(int mode, Rect bounds) {
|
synchronized (mGlobalLock) {
|
setDockedStackCreateStateLocked(mode, bounds);
|
}
|
}
|
|
void setDockedStackCreateStateLocked(int mode, Rect bounds) {
|
mDockedStackCreateMode = mode;
|
mDockedStackCreateBounds = bounds;
|
}
|
|
public void checkSplitScreenMinimizedChanged(boolean animate) {
|
synchronized (mGlobalLock) {
|
final DisplayContent displayContent = getDefaultDisplayContentLocked();
|
displayContent.getDockedDividerController().checkMinimizeChanged(animate);
|
}
|
}
|
|
public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
|
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
|
return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
|
aspectRatio);
|
}
|
|
@Override
|
public void getStackBounds(int windowingMode, int activityType, Rect bounds) {
|
synchronized (mGlobalLock) {
|
final TaskStack 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) {
|
mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
|
DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn));
|
}
|
|
@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) {
|
mRoot.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
|
true /* includingParents */);
|
}
|
}
|
}
|
|
/**
|
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
|
* performance, only one layout pass should be done. This can be called multiple times, and
|
* layouting will be resumed once the last caller has called
|
* {@link #continueSurfaceLayout}.
|
*/
|
void deferSurfaceLayout() {
|
mWindowPlacerLocked.deferLayout();
|
}
|
|
/** Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()} */
|
void continueSurfaceLayout() {
|
mWindowPlacerLocked.continueLayout();
|
}
|
|
/**
|
* 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 boolean isKeyguardTrusted() {
|
synchronized (mGlobalLock) {
|
return mPolicy.isKeyguardTrustedLw();
|
}
|
}
|
|
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");
|
}
|
Preconditions.checkNotNull(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);
|
}
|
}
|
|
public boolean isShowingDream() {
|
synchronized (mGlobalLock) {
|
// TODO(b/123372519): Fix this when dream can be shown on non-default display.
|
return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw();
|
}
|
}
|
|
@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);
|
}
|
}
|
|
public void onKeyguardOccludedChanged(boolean occluded) {
|
synchronized (mGlobalLock) {
|
mPolicy.onKeyguardOccludedChangedLw(occluded);
|
}
|
}
|
|
@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;
|
}
|
}
|
|
void showGlobalActions() {
|
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();
|
mWindowPlacerLocked.performSurfacePlacement();
|
|
// Notify whether the docked stack exists for the current user
|
final DisplayContent displayContent = getDefaultDisplayContentLocked();
|
final TaskStack stack =
|
displayContent.getSplitScreenPrimaryStackIgnoringVisibility();
|
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 isCurrentProfileLocked(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) {
|
if (DEBUG_BOOT) {
|
RuntimeException here = new RuntimeException("here");
|
here.fillInStackTrace();
|
Slog.i(TAG_WM, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled
|
+ " mForceDisplayEnabled=" + mForceDisplayEnabled
|
+ " mShowingBootMessages=" + mShowingBootMessages
|
+ " mSystemBooted=" + mSystemBooted, here);
|
}
|
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() {
|
if (DEBUG_BOOT) {
|
RuntimeException here = new RuntimeException("here");
|
here.fillInStackTrace();
|
Slog.i(TAG_WM, "enableScreenIfNeededLocked: mDisplayEnabled=" + mDisplayEnabled
|
+ " mForceDisplayEnabled=" + mForceDisplayEnabled
|
+ " mShowingBootMessages=" + mShowingBootMessages
|
+ " mSystemBooted=" + mSystemBooted, here);
|
}
|
if (mDisplayEnabled) {
|
return;
|
}
|
if (!mSystemBooted && !mShowingBootMessages) {
|
return;
|
}
|
mH.sendEmptyMessage(H.ENABLE_SCREEN);
|
}
|
|
public void performBootTimeout() {
|
synchronized (mGlobalLock) {
|
if (mDisplayEnabled) {
|
return;
|
}
|
Slog.w(TAG_WM, "***** BOOT TIMEOUT: forcing display enabled");
|
mForceDisplayEnabled = true;
|
}
|
performEnableScreen();
|
}
|
|
/**
|
* Called when System UI has been started.
|
*/
|
public void onSystemUiStarted() {
|
mPolicy.onSystemUiStarted();
|
}
|
|
/*AW_code:add skip animation and scale hook for app-start performance;jiangbin 190328*/
|
public boolean setLaunchValueAnimatorScale(float durationScale) {
|
// Log.w(TAG,"setLaunchValueAnimatorScale "+ durationScale );
|
boolean canchangescale = false;
|
if(((mAnimatorDurationScaleSetting == 1.0f) && (durationScale < 1.0f)) || (durationScale == 1.0f)) {
|
canchangescale = true;
|
if(durationScale == 1.0f) {
|
mH.sendEmptyMessageDelayed(H.NEW_ANIMATOR_SCALE,500);//restore depend mAnimatorDurationScaleSetting
|
|
}else {
|
ValueAnimator.setDurationScale(durationScale);
|
ArrayList<IWindowSessionCallback> callbacks
|
= new ArrayList<IWindowSessionCallback>();
|
synchronized (mWindowMap) {
|
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(durationScale);
|
} catch (RemoteException e) {
|
}
|
}
|
}
|
}
|
// Log.w(TAG,"setLaunchValueAnimatorScale return "+ canchangescale);
|
|
return canchangescale;
|
}
|
|
|
public void setLaunchSkipAnimatorFrame(boolean needskip) {
|
final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
|
//Log.w(TAG,"setLaunchSkipAnimatorFrame "+ needskip );
|
if (flinger != null) {
|
final Parcel data = Parcel.obtain();
|
data.writeInterfaceToken("android.ui.ISurfaceComposer");
|
data.writeFloat(needskip? 0.7f:0.0f);/*52fps->30+fps --0.7f*/
|
try {
|
flinger.transact(1036/*setRefreshSkipCountExtend*/, data, null, 0);
|
} catch (RemoteException ex) {
|
Log.e(TAG, "Failed to set saturation", ex);
|
} finally {
|
data.recycle();
|
}
|
}
|
|
}
|
/*end*/
|
|
|
|
private void performEnableScreen() {
|
synchronized (mGlobalLock) {
|
if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
|
+ " mForceDisplayEnabled=" + mForceDisplayEnabled
|
+ " mShowingBootMessages=" + mShowingBootMessages
|
+ " mSystemBooted=" + mSystemBooted
|
+ " mOnlyCore=" + 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
|
// TODO(multidisplay): Expand to all displays?
|
&& getDefaultDisplayContentLocked().checkWaitingForWindows()) {
|
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.
|
boolean bootanim_exit_delay = mContext.getResources().getBoolean(R.bool.config_delay_exit_bootanim);
|
if (!bootanim_exit_delay)
|
SystemProperties.set("service.bootanim.exit", "1");
|
else
|
mForceDisplayEnabled = true;
|
mBootAnimationStopped = true;
|
}
|
|
// prefinish bootanimation don't check BootAnimationCompleteLocked
|
boolean prefinish_bootanimation = mContext.getResources().getBoolean(R.bool.config_prefinish_bootanimation);
|
if (!prefinish_bootanimation && !mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
|
if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
|
return;
|
}
|
|
try {
|
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
|
if (surfaceFlinger != null) {
|
Slog.i(TAG_WM, "******* 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) {
|
Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
|
}
|
|
EventLog.writeEvent(EventLogTags.WM_BOOT_ANIMATION_DONE, SystemClock.uptimeMillis());
|
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
|
mDisplayEnabled = true;
|
if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** 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);
|
if (DEBUG_BOOT) Slog.i(TAG_WM, "checkBootAnimationComplete: Waiting for anim complete");
|
return false;
|
}
|
if (DEBUG_BOOT) Slog.i(TAG_WM, "checkBootAnimationComplete: Animation complete!");
|
return true;
|
}
|
|
public void showBootMessage(final CharSequence msg, final boolean always) {
|
boolean first = false;
|
synchronized (mGlobalLock) {
|
if (DEBUG_BOOT) {
|
RuntimeException here = new RuntimeException("here");
|
here.fillInStackTrace();
|
Slog.i(TAG_WM, "showBootMessage: msg=" + msg + " always=" + always
|
+ " mAllowBootMessages=" + mAllowBootMessages
|
+ " mShowingBootMessages=" + mShowingBootMessages
|
+ " mSystemBooted=" + mSystemBooted, here);
|
}
|
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() {
|
if (DEBUG_BOOT) {
|
RuntimeException here = new RuntimeException("here");
|
here.fillInStackTrace();
|
Slog.i(TAG_WM, "hideBootMessagesLocked: mDisplayEnabled=" + mDisplayEnabled
|
+ " mForceDisplayEnabled=" + mForceDisplayEnabled
|
+ " mShowingBootMessages=" + mShowingBootMessages
|
+ " mSystemBooted=" + mSystemBooted, here);
|
}
|
if (mShowingBootMessages) {
|
mShowingBootMessages = false;
|
mPolicy.hideBootMessages();
|
}
|
}
|
|
@Override
|
public void setInTouchMode(boolean mode) {
|
synchronized (mGlobalLock) {
|
mInTouchMode = mode;
|
}
|
}
|
|
private void updateCircularDisplayMaskIfNeeded() {
|
if (mContext.getResources().getConfiguration().isScreenRound()
|
&& mContext.getResources().getBoolean(
|
com.android.internal.R.bool.config_windowShowCircularMask)) {
|
final int currentUserId;
|
synchronized (mGlobalLock) {
|
currentUserId = mCurrentUserId;
|
}
|
// Device configuration calls for a circular display mask, but we only enable the mask
|
// if the accessibility color inversion feature is disabled, as the inverted mask
|
// causes artifacts.
|
int inversionState = Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, currentUserId);
|
int showMask = (inversionState == 1) ? 0 : 1;
|
Message m = mH.obtainMessage(H.SHOW_CIRCULAR_DISPLAY_MASK);
|
m.arg1 = showMask;
|
mH.sendMessage(m);
|
}
|
}
|
|
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 showCircularMask(boolean visible) {
|
synchronized (mGlobalLock) {
|
|
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
|
">>> OPEN TRANSACTION showCircularMask(visible=" + visible + ")");
|
openSurfaceTransaction();
|
try {
|
if (visible) {
|
// TODO(multi-display): support multiple displays
|
if (mCircularDisplayMask == null) {
|
int screenOffset = mContext.getResources().getInteger(
|
com.android.internal.R.integer.config_windowOutsetBottom);
|
int maskThickness = mContext.getResources().getDimensionPixelSize(
|
com.android.internal.R.dimen.circular_display_mask_thickness);
|
|
mCircularDisplayMask = new CircularDisplayMask(
|
getDefaultDisplayContentLocked(),
|
mPolicy.getWindowLayerFromTypeLw(
|
WindowManager.LayoutParams.TYPE_POINTER)
|
* TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
|
}
|
mCircularDisplayMask.setVisibility(true);
|
} else if (mCircularDisplayMask != null) {
|
mCircularDisplayMask.setVisibility(false);
|
mCircularDisplayMask = null;
|
}
|
} finally {
|
closeSurfaceTransaction("showCircularMask");
|
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
|
"<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")");
|
}
|
}
|
}
|
|
public void showEmulatorDisplayOverlay() {
|
synchronized (mGlobalLock) {
|
|
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
|
">>> OPEN TRANSACTION showEmulatorDisplayOverlay");
|
openSurfaceTransaction();
|
try {
|
if (mEmulatorDisplayOverlay == null) {
|
mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
|
mContext,
|
getDefaultDisplayContentLocked(),
|
mPolicy.getWindowLayerFromTypeLw(
|
WindowManager.LayoutParams.TYPE_POINTER)
|
* TYPE_LAYER_MULTIPLIER + 10);
|
}
|
mEmulatorDisplayOverlay.setVisibility(true);
|
} finally {
|
closeSurfaceTransaction("showEmulatorDisplayOverlay");
|
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
|
"<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
|
}
|
}
|
}
|
|
// 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,
|
">>> OPEN TRANSACTION showStrictModeViolation");
|
// TODO: Modify this to use the surface trace once it is not going crazy.
|
// b/31532461
|
SurfaceControl.openTransaction();
|
try {
|
// TODO(multi-display): support multiple displays
|
if (mStrictModeFlash == null) {
|
mStrictModeFlash = new StrictModeFlash(
|
getDefaultDisplayContentLocked());
|
}
|
mStrictModeFlash.setVisibility(on);
|
} finally {
|
SurfaceControl.closeTransaction();
|
if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM,
|
"<<< CLOSE TRANSACTION showStrictModeViolation");
|
}
|
}
|
}
|
|
@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);
|
}
|
}
|
|
void setRotateForApp(int displayId,
|
@DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
|
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");
|
}
|
|
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "thawRotation: mRotation="
|
+ 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) {
|
if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
|
+ " alwaysSendConfiguration=" + alwaysSendConfiguration
|
+ " forceRelayout=" + 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 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);
|
}
|
}
|
|
@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) {
|
Slog.w(TAG_WM, "View server did not start");
|
}
|
}
|
return false;
|
}
|
|
try {
|
mViewServer = new ViewServer(this, port);
|
return mViewServer.start();
|
} catch (IOException e) {
|
Slog.w(TAG_WM, "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) {
|
Slog.w(TAG_WM, "Could not send command " + command + " with parameters " + 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);
|
}
|
}
|
|
/**
|
* Instruct the Activity Manager to fetch and update the current display's configuration and
|
* broadcast them to config-changed listeners if appropriate.
|
* NOTE: Can't be called with the window manager lock held since it call into activity manager.
|
*/
|
void sendNewConfiguration(int displayId) {
|
try {
|
final boolean configUpdated = mActivityTaskManager.updateDisplayOverrideConfiguration(
|
null /* values */, displayId);
|
if (!configUpdated) {
|
// Something changed (E.g. device rotation), but no configuration update is needed.
|
// E.g. changing device rotation by 180 degrees. Go ahead and perform surface
|
// placement to unfreeze the display since we froze it when the rotation was updated
|
// in DisplayContent#updateRotationUnchecked.
|
synchronized (mGlobalLock) {
|
final DisplayContent dc = mRoot.getDisplayContent(displayId);
|
if (dc != null && dc.mWaitingForConfig) {
|
dc.mWaitingForConfig = false;
|
mLastFinishedFreezeSource = "config-unchanged";
|
dc.setLayoutNeeded();
|
mWindowPlacerLocked.performSurfacePlacement();
|
}
|
}
|
}
|
} catch (RemoteException e) {
|
}
|
}
|
|
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;
|
}
|
|
TaskStack 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 AppWindowToken focusedApp = topFocusedDisplay.mFocusedApp;
|
return (focusedApp != null && focusedApp.getTask() != null)
|
? focusedApp.getTask().mStack : null;
|
}
|
|
public boolean detectSafeMode() {
|
if (!mInputManagerCallback.waitForInputDevicesReady(
|
INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
|
Slog.w(TAG_WM, "Devices still not ready after waiting "
|
+ INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
|
+ " milliseconds before attempting to detect safe mode.");
|
}
|
|
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) {
|
Log.i(TAG_WM, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
|
+ " dpad=" + dpadState + " trackball=" + 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 {
|
Log.i(TAG_WM, "SAFE MODE not enabled");
|
}
|
mPolicy.setSafeMode(mSafeMode);
|
return mSafeMode;
|
}
|
|
public void displayReady() {
|
synchronized (mGlobalLock) {
|
if (mMaxUiWidth > 0) {
|
mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
|
}
|
final boolean changed = applyForcedPropertiesForDefaultDisplay();
|
mAnimator.ready();
|
mDisplayReady = true;
|
if (changed) {
|
reconfigureDisplayLocked(getDefaultDisplayContentLocked());
|
}
|
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
|
PackageManager.FEATURE_TOUCHSCREEN);
|
}
|
|
try {
|
mActivityTaskManager.updateConfiguration(null);
|
} catch (RemoteException e) {
|
}
|
|
updateCircularDisplayMaskIfNeeded();
|
}
|
|
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 SEND_NEW_CONFIGURATION = 18;
|
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_CIRCULAR_DISPLAY_MASK = 35;
|
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 SET_RUNNING_REMOTE_ANIMATION = 59;
|
public static final int ANIMATION_FAILSAFE = 60;
|
public static final int RECOMPUTE_FOCUS = 61;
|
public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
|
|
/**
|
* 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) {
|
// TODO(multidisplay): Accessibility supported only of default desiplay.
|
if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
|
accessibilityController = mAccessibilityController;
|
}
|
|
lastFocus = displayContent.mLastFocus;
|
newFocus = displayContent.mCurrentFocus;
|
}
|
if (lastFocus == newFocus) {
|
// Focus is not changing, so nothing to do.
|
return;
|
}
|
synchronized (mGlobalLock) {
|
displayContent.mLastFocus = newFocus;
|
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Focus moving from " + lastFocus +
|
" to " + newFocus + " displayId=" + displayContent.getDisplayId());
|
if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {
|
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "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 eventgs.
|
if (accessibilityController != null) {
|
accessibilityController.onWindowFocusChangedNotLocked();
|
}
|
|
if (newFocus != null) {
|
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
|
// if newFocus window is in LosingFocus, remove it.
|
// newFocus window should not be in LosingFocus.
|
displayContent.mLosingFocus.remove(newFocus);
|
newFocus.reportFocusChangedSerialized(true, mInTouchMode);
|
notifyFocusChanged();
|
}
|
|
if (lastFocus != null) {
|
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing focus: " + lastFocus);
|
lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
|
}
|
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++) {
|
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
|
losers.get(i));
|
losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
|
}
|
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) {
|
Slog.w(TAG_WM, "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 SEND_NEW_CONFIGURATION: {
|
final DisplayContent displayContent = (DisplayContent) msg.obj;
|
removeMessages(SEND_NEW_CONFIGURATION, displayContent);
|
if (displayContent.isReady()) {
|
sendNewConfiguration(displayContent.getDisplayId());
|
} else {
|
// Message could come after display has already been removed.
|
if (DEBUG_CONFIGURATION) {
|
final String reason = displayContent.getParent() == null
|
? "detached" : "unready";
|
Slog.w(TAG, "Trying to send configuration to " + reason + " display="
|
+ displayContent);
|
}
|
}
|
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;
|
synchronized (mGlobalLock) {
|
Slog.w(TAG_WM, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);
|
mWaitingForDrawn.clear();
|
callback = mWaitingForDrawnCallback;
|
mWaitingForDrawnCallback = null;
|
}
|
if (callback != null) {
|
callback.run();
|
}
|
break;
|
}
|
|
case SHOW_STRICT_MODE_VIOLATION: {
|
showStrictModeViolation(msg.arg1, msg.arg2);
|
break;
|
}
|
|
case SHOW_CIRCULAR_DISPLAY_MASK: {
|
showCircularMask(msg.arg1 == 1);
|
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;
|
synchronized (mGlobalLock) {
|
callback = mWaitingForDrawnCallback;
|
mWaitingForDrawnCallback = null;
|
}
|
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) {
|
if (DEBUG_BOOT) Slog.i(TAG_WM, "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();
|
displayContent.getDockedDividerController().reevaluateVisibility(false);
|
displayContent.adjustForImeIfNeeded();
|
}
|
break;
|
}
|
case WINDOW_REPLACEMENT_TIMEOUT: {
|
synchronized (mGlobalLock) {
|
for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
|
final AppWindowToken token = mWindowReplacementTimeouts.get(i);
|
token.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.onSeamlessRotationTimeout();
|
}
|
break;
|
}
|
case SET_HAS_OVERLAY_UI: {
|
mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
|
break;
|
}
|
case SET_RUNNING_REMOTE_ANIMATION: {
|
mAmInternal.setRunningRemoteAnimation(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;
|
}
|
}
|
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) {
|
Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + 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)) {
|
Slog.i(TAG_WM, "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;
|
}
|
|
void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) {
|
if (!displayContent.isReady()) {
|
return;
|
}
|
displayContent.configureDisplayPolicy();
|
displayContent.setLayoutNeeded();
|
|
boolean configChanged = displayContent.updateOrientationFromAppTokens();
|
final Configuration currentDisplayConfig = displayContent.getConfiguration();
|
mTempConfiguration.setTo(currentDisplayConfig);
|
displayContent.computeScreenConfiguration(mTempConfiguration);
|
configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
|
|
if (configChanged) {
|
displayContent.mWaitingForConfig = true;
|
startFreezingDisplayLocked(0 /* exitAnim */,
|
0 /* enterAnim */, displayContent);
|
displayContent.sendNewConfiguration();
|
}
|
|
mWindowPlacerLocked.performSurfacePlacement();
|
}
|
|
@Override
|
public void setOverscan(int displayId, int left, int top, int right, int bottom) {
|
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) {
|
DisplayContent displayContent = mRoot.getDisplayContent(displayId);
|
if (displayContent != null) {
|
setOverscanLocked(displayContent, left, top, right, bottom);
|
}
|
}
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
private void setOverscanLocked(DisplayContent displayContent,
|
int left, int top, int right, int bottom) {
|
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
displayInfo.overscanLeft = left;
|
displayInfo.overscanTop = top;
|
displayInfo.overscanRight = right;
|
displayInfo.overscanBottom = bottom;
|
|
mDisplayWindowSettings.setOverscanLocked(displayInfo, left, top, right, bottom);
|
|
reconfigureDisplayLocked(displayContent);
|
}
|
|
@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 (localLOGV) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);
|
if (win == null) {
|
if (throwOnError) {
|
throw new IllegalArgumentException(
|
"Requested window " + client + " does not exist");
|
}
|
Slog.w(TAG_WM, "Failed looking up window callers=" + 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);
|
}
|
Slog.w(TAG_WM, "Failed looking up window callers=" + 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) {
|
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + 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 (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
|
return;
|
}
|
for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {
|
WindowState win = mWaitingForDrawn.get(j);
|
if (DEBUG_SCREEN_ON) Slog.i(TAG_WM, "Waiting for drawn " + win +
|
": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
|
" mHasSurface=" + win.mHasSurface +
|
" drawState=" + win.mWinAnimator.mDrawState);
|
if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
|
// Window has been removed or hidden; no draw will now happen, so stop waiting.
|
if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);
|
mWaitingForDrawn.remove(win);
|
} else if (win.hasDrawnLw()) {
|
// Window is now drawn (and shown).
|
if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "Window drawn win=" + win);
|
mWaitingForDrawn.remove(win);
|
}
|
}
|
if (mWaitingForDrawn.isEmpty()) {
|
if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "All windows drawn!");
|
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
|
mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);
|
}
|
}
|
|
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) {
|
if (DEBUG_KEEP_SCREEN_ON) {
|
Slog.d(TAG_KEEP_SCREEN_ON, "Acquiring screen wakelock due to "
|
+ mRoot.mHoldScreenWindow);
|
}
|
mLastWakeLockHoldingWindow = mRoot.mHoldScreenWindow;
|
mLastWakeLockObscuringWindow = null;
|
mHoldingScreenWakeLock.acquire();
|
mPolicy.keepScreenOnStartedLw();
|
} else {
|
if (DEBUG_KEEP_SCREEN_ON) {
|
Slog.d(TAG_KEEP_SCREEN_ON, "Releasing screen wakelock, obscured by "
|
+ mRoot.mObscuringWindow);
|
}
|
mLastWakeLockHoldingWindow = null;
|
mLastWakeLockObscuringWindow = mRoot.mObscuringWindow;
|
mPolicy.keepScreenOnStoppedLw();
|
mHoldingScreenWakeLock.release();
|
}
|
}
|
}
|
|
void requestTraversal() {
|
synchronized (mGlobalLock) {
|
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 || mRotatingSeamlessly) {
|
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;
|
}
|
// Send interaction hint to improve redraw performance.re-use launch mode;
|
// reset in stopfreezing
|
if(mPowerManagerInternal.getPowerSceneMode()!= PowerManager.BENCHMARK) {
|
mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);/*start rotation-freez*/
|
}
|
|
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
|
"startFreezingDisplayLocked: exitAnim="
|
+ exitAnim + " enterAnim=" + enterAnim
|
+ " called by " + 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);
|
if (CUSTOM_SCREEN_ROTATION) {
|
mExitAnimId = exitAnim;
|
mEnterAnimId = enterAnim;
|
ScreenRotationAnimation screenRotationAnimation =
|
mAnimator.getScreenRotationAnimationLocked(mFrozenDisplayId);
|
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);
|
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
|
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) {
|
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
|
"stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
|
+ ", mAppsFreezingScreen=" + mAppsFreezingScreen
|
+ ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
|
+ ", mClientFreezingScreen=" + mClientFreezingScreen
|
+ ", mOpeningApps.size()=" + numOpeningApps);
|
return;
|
}
|
|
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
|
"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);
|
}
|
Slog.i(TAG_WM, sb.toString());
|
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
|
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
|
if (PROFILE_ORIENTATION) {
|
Debug.stopMethodTracing();
|
}
|
|
boolean updateRotation = false;
|
|
ScreenRotationAnimation screenRotationAnimation =
|
mAnimator.getScreenRotationAnimationLocked(displayId);
|
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
|
&& screenRotationAnimation.hasScreenshot()) {
|
if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
|
DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
// Get rotation animation again, with new top window
|
if (!displayContent.getDisplayPolicy()
|
.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
|
mExitAnimId = mEnterAnimId = 0;
|
}
|
if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
|
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
|
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
|
mTransaction.apply();
|
scheduleAnimationLocked();
|
} else {
|
screenRotationAnimation.kill();
|
mAnimator.setScreenRotationAnimationLocked(displayId, null);
|
updateRotation = true;
|
}
|
} else {
|
if (screenRotationAnimation != null) {
|
screenRotationAnimation.kill();
|
mAnimator.setScreenRotationAnimationLocked(displayId, 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.updateOrientationFromAppTokens();
|
|
// 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 && updateRotation) {
|
if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "Performing post-rotate rotation");
|
configChanged |= displayContent.updateRotationUnchecked();
|
}
|
|
if (configChanged) {
|
displayContent.sendNewConfiguration();
|
}
|
mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
|
|
if(mPowerManagerInternal.getPowerSceneMode()!= PowerManager.BENCHMARK) {
|
mPowerManagerInternal.powerHint(PowerManager.NORMAL,PowerHint.LAUNCH);/*end rotation*/
|
}
|
}
|
|
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 createWatermarkInTransaction() {
|
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(displayContent, displayContent.mRealDisplayMetrics,
|
toks);
|
}
|
}
|
} 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 setShelfHeight(boolean visible, int shelfHeight) {
|
mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
|
"setShelfHeight()");
|
synchronized (mGlobalLock) {
|
getDefaultDisplayContentLocked().getPinnedStackController().setAdjustedForShelf(visible,
|
shelfHeight);
|
}
|
}
|
|
@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 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) {
|
mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
|
DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show));
|
}
|
}
|
|
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;
|
}
|
}
|
|
public void notifyAppRelaunching(IBinder token) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
|
if (appWindow != null) {
|
appWindow.startRelaunching();
|
}
|
}
|
}
|
|
public void notifyAppRelaunchingFinished(IBinder token) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
|
if (appWindow != null) {
|
appWindow.finishRelaunching();
|
}
|
}
|
}
|
|
public void notifyAppRelaunchesCleared(IBinder token) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
|
if (appWindow != null) {
|
appWindow.clearRelaunching();
|
}
|
}
|
}
|
|
public void notifyAppResumedFinished(IBinder token) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
|
if (appWindow != null) {
|
appWindow.getDisplayContent().mUnknownAppVisibilityController
|
.notifyAppResumedFinished(appWindow);
|
}
|
}
|
}
|
|
/**
|
* Called when a task has been removed from the recent tasks list.
|
* <p>
|
* Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
|
* container may not exist when this happens.
|
*/
|
public void notifyTaskRemovedFromRecents(int taskId, int userId) {
|
synchronized (mGlobalLock) {
|
mTaskSnapshotController.notifyTaskRemovedFromRecents(taskId, userId);
|
}
|
}
|
|
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 dumpTraceStatus(PrintWriter pw) {
|
pw.println("WINDOW MANAGER TRACE (dumpsys window trace)");
|
pw.print(mWindowTracing.getStatus() + "\n");
|
}
|
|
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 writeToProtoLocked(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
|
mPolicy.writeToProto(proto, POLICY);
|
mRoot.writeToProto(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());
|
}
|
|
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 (mWaitingForDrawn.size() > 0) {
|
pw.println();
|
pw.println(" Clients waiting for these windows to be drawn:");
|
for (int i=mWaitingForDrawn.size()-1; i>=0; i--) {
|
WindowState win = 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(" mLastWindowForcedOrientation=");
|
pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
|
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.mAppToken != 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 appWindowToken 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(AppWindowToken appWindowToken, 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 (appWindowToken != null) {
|
pw.println(" Application at fault: " + appWindowToken.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) {
|
writeToProtoLocked(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 {
|
// 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);
|
}
|
}
|
|
// 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();
|
}
|
}
|
|
public void onDisplayChanged(int displayId) {
|
synchronized (mGlobalLock) {
|
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
|
if (displayContent != null) {
|
displayContent.updateDisplayInfo();
|
}
|
mWindowPlacerLocked.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.
|
*/
|
public void setWillReplaceWindow(IBinder token, boolean animate) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
|
if (appWindowToken == null) {
|
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
|
+ token);
|
return;
|
}
|
if (!appWindowToken.hasContentToDisplay()) {
|
Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
|
+ token);
|
return;
|
}
|
appWindowToken.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 AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
|
if (appWindowToken == null) {
|
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
|
+ token);
|
return;
|
}
|
if (!appWindowToken.hasContentToDisplay()) {
|
Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
|
+ token);
|
return;
|
}
|
|
if (childrenOnly) {
|
appWindowToken.setWillReplaceChildWindows();
|
} else {
|
appWindowToken.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.
|
*/
|
public void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
|
synchronized (mGlobalLock) {
|
final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
|
if (appWindowToken == null) {
|
Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
|
+ token);
|
return;
|
}
|
if (replacing) {
|
scheduleWindowReplacementTimeouts(appWindowToken);
|
} else {
|
appWindowToken.clearWillReplaceWindows();
|
}
|
}
|
}
|
|
void scheduleWindowReplacementTimeouts(AppWindowToken appWindowToken) {
|
if (!mWindowReplacementTimeouts.contains(appWindowToken)) {
|
mWindowReplacementTimeouts.add(appWindowToken);
|
}
|
mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
|
mH.sendEmptyMessageDelayed(
|
H.WINDOW_REPLACEMENT_TIMEOUT, WINDOW_REPLACEMENT_TIMEOUT_DURATION);
|
}
|
|
@Override
|
public int getDockedStackSide() {
|
synchronized (mGlobalLock) {
|
final TaskStack dockedStack = getDefaultDisplayContentLocked()
|
.getSplitScreenPrimaryStackIgnoringVisibility();
|
return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
|
}
|
}
|
|
public void setDockedStackResizing(boolean resizing) {
|
synchronized (mGlobalLock) {
|
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);
|
}
|
}
|
|
public void setForceResizableTasks(boolean forceResizableTasks) {
|
synchronized (mGlobalLock) {
|
mForceResizableTasks = forceResizableTasks;
|
}
|
}
|
|
public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
|
synchronized (mGlobalLock) {
|
mSupportsPictureInPicture = supportsPictureInPicture;
|
}
|
}
|
|
public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
|
synchronized (mGlobalLock) {
|
mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
|
}
|
}
|
|
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
|
synchronized (mGlobalLock) {
|
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
|
}
|
}
|
|
public 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 (!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) {
|
Slog.w(TAG_WM, "Bad requesting window " + 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) {
|
Slog.w(TAG_WM, "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) {
|
Slog.w(TAG_WM, "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) {
|
Slog.w(TAG_WM, "Bad requesting window " + client);
|
return;
|
}
|
getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc);
|
} 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) {
|
Slog.w(TAG_WM, "Bad requesting window " + 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, int regionId, Region region) {
|
synchronized (mGlobalLock) {
|
final WindowState callingWin = windowForClientLocked(null, client, false);
|
if (callingWin == null) {
|
Slog.w(TAG_WM, "Bad requesting window " + client);
|
return;
|
}
|
callingWin.updateTapExcludeRegion(regionId, 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) {
|
Slog.w(TAG_WM, "Attempted to get windowing mode of a display that does not exist: "
|
+ 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) {
|
Slog.w(TAG_WM, "Attempted to set windowing mode to a display that does not exist: "
|
+ displayId);
|
return;
|
}
|
|
int lastWindowingMode = displayContent.getWindowingMode();
|
mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode);
|
|
reconfigureDisplayLocked(displayContent);
|
|
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.
|
mH.removeMessages(H.SEND_NEW_CONFIGURATION);
|
|
final long origId = Binder.clearCallingIdentity();
|
try {
|
// direct call since lock is shared.
|
sendNewConfiguration(displayId);
|
} 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) {
|
Slog.w(TAG_WM, "Attempted to get remove mode of a display that does not exist: "
|
+ 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) {
|
Slog.w(TAG_WM, "Attempted to set remove mode to a display that does not exist: "
|
+ displayId);
|
return;
|
}
|
|
mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode);
|
|
reconfigureDisplayLocked(displayContent);
|
}
|
}
|
|
@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) {
|
Slog.w(TAG_WM, "Attempted to get flag of a display that does not exist: "
|
+ 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");
|
}
|
|
synchronized (mGlobalLock) {
|
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
|
if (displayContent == null) {
|
Slog.w(TAG_WM, "Attempted to set flag to a display that does not exist: "
|
+ displayId);
|
return;
|
}
|
|
mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent,
|
shouldShow);
|
|
reconfigureDisplayLocked(displayContent);
|
}
|
}
|
|
@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) {
|
Slog.w(TAG_WM, "Attempted to get system decors flag of a display that does "
|
+ "not exist: " + 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");
|
}
|
|
synchronized (mGlobalLock) {
|
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
|
if (displayContent == null) {
|
Slog.w(TAG_WM, "Attempted to set system decors flag to a display that does "
|
+ "not exist: " + displayId);
|
return;
|
}
|
if (displayContent.isUntrustedVirtualDisplay()) {
|
throw new SecurityException("Attempted to set system decors flag to an untrusted "
|
+ "virtual display: " + displayId);
|
}
|
|
mDisplayWindowSettings.setShouldShowSystemDecorsLocked(displayContent, shouldShow);
|
|
reconfigureDisplayLocked(displayContent);
|
}
|
}
|
|
@Override
|
public boolean shouldShowIme(int displayId) {
|
if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowIme()")) {
|
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
|
}
|
|
synchronized (mGlobalLock) {
|
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
|
if (displayContent == null) {
|
Slog.w(TAG_WM, "Attempted to get IME flag of a display that does not exist: "
|
+ displayId);
|
return false;
|
}
|
if (displayContent.isUntrustedVirtualDisplay()) {
|
return false;
|
}
|
return mDisplayWindowSettings.shouldShowImeLocked(displayContent)
|
|| mForceDesktopModeOnExternalDisplays;
|
}
|
}
|
|
@Override
|
public void setShouldShowIme(int displayId, boolean shouldShow) {
|
if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowIme()")) {
|
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
|
}
|
|
synchronized (mGlobalLock) {
|
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
|
if (displayContent == null) {
|
Slog.w(TAG_WM, "Attempted to set IME flag to a display that does not exist: "
|
+ displayId);
|
return;
|
}
|
if (displayContent.isUntrustedVirtualDisplay()) {
|
throw new SecurityException("Attempted to set IME flag to an untrusted "
|
+ "virtual display: " + displayId);
|
}
|
|
mDisplayWindowSettings.setShouldShowImeLocked(displayContent, shouldShow);
|
|
reconfigureDisplayLocked(displayContent);
|
}
|
}
|
|
@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);
|
}
|
|
@Override
|
public void requestUserActivityNotification() {
|
if (!checkCallingPermission(android.Manifest.permission.USER_ACTIVITY,
|
"requestUserActivityNotification()")) {
|
throw new SecurityException("Requires USER_ACTIVITY permission");
|
}
|
mPolicy.requestUserActivityNotification();
|
}
|
|
void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
|
if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
|
return;
|
}
|
w.mSeamlesslyRotated = seamlesslyRotated;
|
if (seamlesslyRotated) {
|
mSeamlessRotationCount++;
|
} else {
|
mSeamlessRotationCount--;
|
}
|
if (mSeamlessRotationCount == 0) {
|
if (DEBUG_ORIENTATION) {
|
Slog.i(TAG, "Performing post-rotate rotation after seamless rotation");
|
}
|
finishSeamlessRotation();
|
|
w.getDisplayContent().updateRotationAndSendNewConfigIfNeeded();
|
}
|
}
|
|
private final class LocalService extends WindowManagerInternal {
|
@Override
|
public void requestTraversalFromDisplayManager() {
|
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 void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
|
synchronized (mGlobalLock) {
|
if (mAccessibilityController == null) {
|
mAccessibilityController = new AccessibilityController(
|
WindowManagerService.this);
|
}
|
mAccessibilityController.setWindowsForAccessibilityCallback(callback);
|
if (!mAccessibilityController.hasCallbacksLocked()) {
|
mAccessibilityController = null;
|
}
|
}
|
}
|
|
@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) {
|
boolean allWindowsDrawn = false;
|
synchronized (mGlobalLock) {
|
mWaitingForDrawnCallback = callback;
|
getDefaultDisplayContentLocked().waitForAllWindowsDrawn();
|
mWindowPlacerLocked.requestTraversal();
|
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
|
if (mWaitingForDrawn.isEmpty()) {
|
allWindowsDrawn = true;
|
} else {
|
mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, 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) {
|
Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
|
+ " for non-exiting displayId=" + displayId);
|
return;
|
}
|
|
final WindowToken token = dc.removeWindowToken(binder);
|
if (token == null) {
|
Slog.w(TAG_WM, "removeWindowToken: Attempted to remove non-existing token: "
|
+ 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);
|
}
|
}
|
|
@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() {
|
final AccessibilityController accessibilityController;
|
synchronized (mGlobalLock) {
|
accessibilityController = mAccessibilityController;
|
}
|
if (accessibilityController != null) {
|
accessibilityController.performComputeChangedWindowsNotLocked(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 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 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));
|
}
|
}
|
}
|
|
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) {
|
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 mSurfaceBuilderFactory.make(s);
|
}
|
|
void sendSetRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
|
mH.obtainMessage(H.SET_RUNNING_REMOTE_ANIMATION, pid, runningRemoteAnimation ? 1 : 0)
|
.sendToTarget();
|
}
|
|
void startSeamlessRotation() {
|
// We are careful to reset this in case a window was removed before it finished
|
// seamless rotation.
|
mSeamlessRotationCount = 0;
|
|
mRotatingSeamlessly = true;
|
}
|
|
boolean isRotatingSeamlessly() {
|
return mRotatingSeamlessly;
|
}
|
|
void finishSeamlessRotation() {
|
mRotatingSeamlessly = false;
|
}
|
|
/**
|
* 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) {
|
mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer(
|
DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState));
|
}
|
}
|
|
/**
|
* 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());
|
}
|
new SurfaceControl.Transaction().syncInputWindows().apply(true);
|
}
|
|
private void waitForAnimationsToComplete() {
|
synchronized (mGlobalLock) {
|
long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS;
|
while (mRoot.isSelfOrChildAnimating() && timeoutRemaining > 0) {
|
long startTime = System.currentTimeMillis();
|
try {
|
mGlobalLock.wait(timeoutRemaining);
|
} catch (InterruptedException e) {
|
}
|
timeoutRemaining -= (System.currentTimeMillis() - startTime);
|
}
|
|
if (mRoot.isSelfOrChildAnimating()) {
|
Log.w(TAG, "Timed out waiting for animations to complete.");
|
}
|
}
|
}
|
|
void onAnimationFinished() {
|
synchronized (mGlobalLock) {
|
mGlobalLock.notifyAll();
|
}
|
}
|
|
private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
|
final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
|
if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
|
return;
|
}
|
|
handleTaskFocusChange(touchedWindow.getTask());
|
handleDisplayFocusChange(touchedWindow);
|
}
|
|
private void handleTaskFocusChange(Task task) {
|
if (task == null) {
|
return;
|
}
|
|
final TaskStack stack = task.mStack;
|
// 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) {
|
}
|
}
|
|
private void handleDisplayFocusChange(WindowState window) {
|
final DisplayContent displayContent = window.getDisplayContent();
|
if (displayContent == null) {
|
return;
|
}
|
|
if (!window.canReceiveKeys()) {
|
// 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 WindowContainer parent = displayContent.getParent();
|
if (parent != null && parent.getTopChild() != displayContent) {
|
parent.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
|
true /* includingParents */);
|
// For compatibility, only the topmost activity is allowed to be resumed for pre-Q
|
// app. Ensure the topmost activities are resumed whenever a display is moved to top.
|
// TODO(b/123761773): Investigate whether we can move this into
|
// RootActivityContainer#updateTopResumedActivityIfNeeded(). Currently, it is risky
|
// to do so because it seems possible to resume activities as part of a larger
|
// transaction and it's too early to resume based on current order when performing
|
// updateTopResumedActivityIfNeeded().
|
displayContent.mAcitvityDisplay.ensureActivitiesVisible(null /* starting */,
|
0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
|
}
|
}
|
}
|