/* * 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 android.Manifest; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.InputManager; 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.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.PowerManagerInternal; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.SystemService; import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.TypedValue; import android.view.AppTransitionAnimationSpec; import android.view.Choreographer; import android.view.Display; import android.view.DisplayInfo; import android.view.Gravity; import android.view.PointerIcon; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.IDockedStackListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; 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.KeyEvent; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.Surface; import android.view.Surface.OutOfResourcesException; 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.WindowManagerGlobal; import android.view.WindowManagerInternal; import android.view.WindowManagerPolicy; import android.view.WindowManagerPolicy.PointerEventListener; import android.view.animation.Animation; import android.view.inputmethod.InputMethodManagerInternal; import com.android.internal.R; import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IShortcutService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.AttributeCache; 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.PhoneWindowManager; import com.android.server.power.ShutdownThread; 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.HashMap; import java.util.Iterator; import java.util.List; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.StatusBarManager.DISABLE_MASK; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; 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_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.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_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_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_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static android.view.WindowManagerPolicy.TRANSIT_EXIT; import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE; import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END; import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM; 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_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; 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_DRAG; 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_KEYGUARD; 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_RESIZE; 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_STACK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; 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_SURFACE_ALLOC; 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.WindowStateAnimator.DRAW_PENDING; /** {@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 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; 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; private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f; private static final String PROPERTY_BUILD_DATE_UTC = "ro.build.date.utc"; // 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; final private KeyguardDisableHandler mKeyguardDisableHandler; final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { mKeyguardDisableHandler.sendEmptyMessage( KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED); } } }; final WindowSurfacePlacer mWindowPlacerLocked; /** * 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 mHaveInputMethods; final boolean mHasPermanentDpad; final long mDrawLockTimeoutMillis; final boolean mAllowAnimationsInLowPowerMode; final boolean mAllowBootMessages; final boolean mLimitedAlphaCompositing; final WindowManagerPolicy mPolicy = new PhoneWindowManager(); final IActivityManager mActivityManager; final ActivityManagerInternal mAmInternal; final AppOpsManager mAppOps; final DisplaySettings mDisplaySettings; /** * All currently active sessions with clients. */ final ArraySet mSessions = new ArraySet<>(); /** * Mapping from an IWindow IBinder to the server's Window object. * This is also used as the lock for all of our state. * NOTE: Never call into methods that lock ActivityManagerService while holding this object. */ final HashMap mWindowMap = new HashMap<>(); /** * Mapping from a token IBinder to a WindowToken object. */ final HashMap mTokenMap = new HashMap<>(); /** * List of window tokens that have finished starting their application, * and now need to have the policy remove their windows. */ final ArrayList mFinishedStarting = new ArrayList<>(); /** * List of window tokens that have finished drawing their own windows and * no longer need to show any saved surfaces. Windows that's still showing * saved surfaces will be cleaned up after next animation pass. */ final ArrayList mFinishedEarlyAnim = new ArrayList<>(); /** * 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 mReplacingWindowTimeouts = new ArrayList<>(); /** * The input consumer added to the window manager which consumes input events to windows below * it. */ InputConsumerImpl mInputConsumer; /** * The input consumer added to the window manager before all wallpaper windows. */ InputConsumerImpl mWallpaperInputConsumer; /** * 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 mResizingWindows = new ArrayList<>(); /** * Windows whose animations have ended and now must be removed. */ final ArrayList mPendingRemove = new ArrayList<>(); /** * Used when processing mPendingRemove to avoid working on the original array. */ WindowState[] mPendingRemoveTmp = new WindowState[20]; /** * Windows whose surface should be destroyed. */ final ArrayList 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 mDestroyPreservedSurface = new ArrayList<>(); /** * Windows that have lost input focus and are waiting for the new * focus window to be displayed before they are told about this. */ ArrayList mLosingFocus = 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 mForceRemoves = new ArrayList<>(); /** * Windows that clients are waiting to have drawn. */ ArrayList mWaitingForDrawn = new ArrayList<>(); /** * And the callback to make when they've all been drawn. */ Runnable mWaitingForDrawnCallback; /** * Used when rebuilding window list to keep track of windows that have * been removed. */ WindowState[] mRebuildTmp = new WindowState[20]; /** * Stores for each user whether screencapture is disabled * This array is essentially a cache for all userId for * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled} */ SparseArray mScreenCaptureDisabled = new SparseArray<>(); IInputMethodManager mInputMethodManager; AccessibilityController mAccessibilityController; final SurfaceSession mFxSession; 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(); boolean mDisplayReady; boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; boolean mForceDisplayEnabled = false; boolean mShowingBootMessages = false; boolean mBootAnimationStopped = 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; /** All DisplayContents in the world, kept here */ SparseArray mDisplayContents = new SparseArray<>(2); int mRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; private boolean mKeyguardWaitingForActivityDrawn; int mDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; Rect mDockedStackCreateBounds; private final SparseIntArray mTmpTaskIds = new SparseIntArray(); private final ArrayList mChangedStackList = new ArrayList(); boolean mForceResizableTasks = false; int getDragLayerLocked() { return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; } class RotationWatcher { IRotationWatcher watcher; IBinder.DeathRecipient deathRecipient; RotationWatcher(IRotationWatcher w, IBinder.DeathRecipient d) { watcher = w; deathRecipient = d; } } ArrayList mRotationWatchers = new ArrayList<>(); int mDeferredRotationPauseCount; int mSystemDecorLayer = 0; final Rect mScreenRect = new Rect(); boolean mDisplayFrozen = false; long mDisplayFreezeTime = 0; int mLastDisplayFreezeDuration = 0; Object mLastFinishedFreezeSource = null; boolean mWaitingForConfig = 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; int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int mLayoutSeq = 0; // Last systemUiVisibility we received from status bar. int mLastStatusBarVisibility = 0; // Last systemUiVisibility we dispatched to windows. int mLastDispatchedSystemUiVisibility = 0; // State while inside of layoutAndPlaceSurfacesLocked(). boolean mFocusMayChange; Configuration mCurConfiguration = new Configuration(); // 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 AppTransition mAppTransition; boolean mSkipAppTransitionAnimation = false; final ArraySet mOpeningApps = new ArraySet<>(); final ArraySet mClosingApps = new ArraySet<>(); boolean mIsTouchDevice; final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics(); final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics(); final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics(); final H mH = new H(); final Choreographer mChoreographer = Choreographer.getInstance(); WindowState mCurrentFocus = null; WindowState mLastFocus = null; /** This just indicates the window the input method is on top of, not * necessarily the window its input is going to. */ WindowState mInputMethodTarget = null; /** If true hold off on modifying the animation layer of mInputMethodTarget */ boolean mInputMethodTargetWaitingAnim; WindowState mInputMethodWindow = null; final ArrayList mInputMethodDialogs = new ArrayList<>(); /** Temporary list for comparison. Always clear this after use so we don't end up with * orphaned windows references */ final ArrayList mTmpWindows = new ArrayList<>(); boolean mHardKeyboardAvailable; WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; SettingsObserver mSettingsObserver; 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); 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); } @Override public void onChange(boolean selfChange, Uri uri) { if (uri == null) { return; } if (mDisplayInversionEnabledUri.equals(uri)) { updateCircularDisplayMaskIfNeeded(); } else { @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); } } } WallpaperController mWallpaperControllerLocked; final WindowLayersController mLayersController; boolean mAnimateWallpaperWithTarget; AppWindowToken mFocusedApp = null; PowerManager mPowerManager; PowerManagerInternal mPowerManagerInternal; float mWindowAnimationScaleSetting = 1.0f; float mTransitionAnimationScaleSetting = 1.0f; float mAnimatorDurationScaleSetting = 1.0f; boolean mAnimationsDisabled = false; final InputManagerService mInputManager; final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; final Display[] mDisplays; // Who is holding the screen on. Session mHoldingScreenOn; PowerManager.WakeLock mHoldingScreenWakeLock; boolean mTurnOnScreen; // Whether or not a layout can cause a wake up when theater mode is enabled. boolean mAllowTheaterModeWakeFromLayout; TaskPositioner mTaskPositioner; DragState mDragState = null; // For frozen screen animations. int mExitAnimId, mEnterAnimId; boolean mAnimationScheduled; /** 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; private final BoundsAnimationController mBoundsAnimationController; SparseArray mTaskIdToTask = new SparseArray<>(); /** All of the TaskStacks in the window manager, unordered. For an ordered list call * DisplayContent.getStacks(). */ SparseArray mStackIdToStack = new SparseArray<>(); private final PointerEventDispatcher mPointerEventDispatcher; private WindowContentFrameStats mTempWindowRenderStats; final class DragInputEventReceiver extends InputEventReceiver { // Set, if stylus button was down at the start of the drag. private boolean mStylusButtonDownAtStart; // Indicates the first event to check for button state. private boolean mIsStartEvent = true; public DragInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event) { boolean handled = false; try { if (event instanceof MotionEvent && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 && mDragState != null) { final MotionEvent motionEvent = (MotionEvent)event; boolean endDrag = false; final float newX = motionEvent.getRawX(); final float newY = motionEvent.getRawY(); final boolean isStylusButtonDown = (motionEvent.getButtonState() & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0; if (mIsStartEvent) { if (isStylusButtonDown) { // First event and the button was down, check for the button being // lifted in the future, if that happens we'll drop the item. mStylusButtonDownAtStart = true; } mIsStartEvent = false; } switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: { if (DEBUG_DRAG) { Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer"); } } break; case MotionEvent.ACTION_MOVE: { if (mStylusButtonDownAtStart && !isStylusButtonDown) { if (DEBUG_DRAG) Slog.d(TAG_WM, "Button no longer pressed; dropping at " + newX + "," + newY); synchronized (mWindowMap) { endDrag = mDragState.notifyDropLw(newX, newY); } } else { synchronized (mWindowMap) { // move the surface and tell the involved window(s) where we are mDragState.notifyMoveLw(newX, newY); } } } break; case MotionEvent.ACTION_UP: { if (DEBUG_DRAG) Slog.d(TAG_WM, "Got UP on move channel; dropping at " + newX + "," + newY); synchronized (mWindowMap) { endDrag = mDragState.notifyDropLw(newX, newY); } } break; case MotionEvent.ACTION_CANCEL: { if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!"); endDrag = true; } break; } if (endDrag) { if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag ended; tearing down state"); // tell all the windows that the drag has ended synchronized (mWindowMap) { mDragState.endDragLw(); } mStylusButtonDownAtStart = false; mIsStartEvent = true; } handled = true; } } catch (Exception e) { Slog.e(TAG_WM, "Exception caught by drag handleMotion", e); } finally { finishInputEvent(event, handled); } } } /** * 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 mWindowChangeListeners = new ArrayList<>(); boolean mWindowsChanged = false; public interface WindowChangeListener { public void windowsChanged(); public void focusChanged(); } final Configuration mTempConfiguration = new Configuration(); // The desired scaling factor for compatible apps. float mCompatibleScreenScale; // 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; // List of clients without a transtiton animation that we notify once we are done transitioning // since they won't be notified through the app window animator. final List mNoAnimationNotifyOnTransitionFinished = new ArrayList<>(); // List of displays to reconfigure after configuration changes. // Some of the information reported for a display is dependent on resources to do the right // calculations. For example, {@link DisplayInfo#smallestNominalAppWidth} and company are // dependent on the height and width of the status and nav bar which change depending on the // current configuration. private final DisplayContentList mReconfigureOnConfigurationChanged = new DisplayContentList(); /** Listener to notify activity manager about app transitions. */ private final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier = new WindowManagerInternal.AppTransitionListener() { @Override public void onAppTransitionCancelledLocked() { mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_CANCELLED); } @Override public void onAppTransitionFinishedLocked(IBinder token) { mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_FINISHED); AppWindowToken atoken = findAppWindowToken(token); if (atoken == null) { return; } if (atoken.mLaunchTaskBehind) { try { mActivityManager.notifyLaunchTaskBehindComplete(atoken.token); } catch (RemoteException e) { } atoken.mLaunchTaskBehind = false; } else { atoken.updateReportedVisibilityLocked(); if (atoken.mEnteringAnimation) { atoken.mEnteringAnimation = false; try { mActivityManager.notifyEnterAnimationComplete(atoken.token); } catch (RemoteException e) { } } } } }; public static WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; DisplayThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { holder[0] = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0]; } 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); } private WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { mContext = context; mHaveInputMethods = haveInputMethods; 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); mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplaySettings = new DisplaySettings(); mDisplaySettings.readSettingsLocked(); mWallpaperControllerLocked = new WallpaperController(this); mWindowPlacerLocked = new WindowSurfacePlacer(this); mLayersController = new WindowLayersController(this); LocalServices.addService(WindowManagerPolicy.class, mPolicy); mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG_WM)); mFxSession = new SurfaceSession(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mDisplays = mDisplayManager.getDisplays(); for (Display display : mDisplays) { createDisplayContentLocked(display); } mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mPowerManagerInternal.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public void onLowPowerModeChanged(boolean enabled) { synchronized (mWindowMap) { if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) { mAnimationsDisabled = enabled; dispatchNewAnimatorScaleLocked(null); } } } }); mAnimationsDisabled = mPowerManagerInternal.getLowPowerModeEnabled(); mScreenFrozenLock = mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); mScreenFrozenLock.setReferenceCounted(false); mAppTransition = new AppTransition(context, this); mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier); mBoundsAnimationController = new BoundsAnimationController(mAppTransition, UiThread.getHandler()); mActivityManager = ActivityManagerNative.getDefault(); mAmInternal = LocalServices.getService(ActivityManagerInternal.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(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, null, opListener); mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener); // Get persisted window scale setting mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting); mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScaleSetting); setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting)); // Track changes to DevicePolicyManager state so we can enable/disable keyguard. IntentFilter filter = new IntentFilter(); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); mSettingsObserver = new SettingsObserver(); mHoldingScreenWakeLock = mPowerManager.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM); mHoldingScreenWakeLock.setReferenceCounted(false); mAnimator = new WindowAnimator(this); mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); LocalServices.addService(WindowManagerInternal.class, new LocalService()); initPolicy(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); SurfaceControl.openTransaction(); try { createWatermarkInTransaction(); } finally { SurfaceControl.closeTransaction(); } showEmulatorDisplayOverlayIfNeeded(); } public InputMonitor getInputMonitor() { return mInputMonitor; } @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; } } private void placeWindowAfter(WindowState pos, WindowState window) { final WindowList windows = pos.getWindowList(); final int i = windows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG_WM, "Adding window " + window + " at " + (i+1) + " of " + windows.size() + " (after " + pos + ")"); windows.add(i+1, window); mWindowsChanged = true; } private void placeWindowBefore(WindowState pos, WindowState window) { final WindowList windows = pos.getWindowList(); int i = windows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG_WM, "Adding window " + window + " at " + i + " of " + windows.size() + " (before " + pos + ")"); if (i < 0) { Slog.w(TAG_WM, "placeWindowBefore: Unable to find " + pos + " in " + windows); i = 0; } windows.add(i, window); mWindowsChanged = true; } //This method finds out the index of a window that has the same app token as //win. used for z ordering the windows in mWindows private int findIdxBasedOnAppTokens(WindowState win) { WindowList windows = win.getWindowList(); for(int j = windows.size() - 1; j >= 0; j--) { WindowState wentry = windows.get(j); if(wentry.mAppToken == win.mAppToken) { return j; } } return -1; } /** * Return the list of Windows from the passed token on the given Display. * @param token The token with all the windows. * @param displayContent The display we are interested in. * @return List of windows from token that are on displayContent. */ private WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) { final WindowList windowList = new WindowList(); final int count = token.windows.size(); for (int i = 0; i < count; i++) { final WindowState win = token.windows.get(i); if (win.getDisplayContent() == displayContent) { windowList.add(win); } } return windowList; } /** * Recursive search through a WindowList and all of its windows' children. * @param targetWin The window to search for. * @param windows The list to search. * @return The index of win in windows or of the window that is an ancestor of win. */ private int indexOfWinInWindowList(WindowState targetWin, WindowList windows) { for (int i = windows.size() - 1; i >= 0; i--) { final WindowState w = windows.get(i); if (w == targetWin) { return i; } if (!w.mChildWindows.isEmpty()) { if (indexOfWinInWindowList(targetWin, w.mChildWindows) >= 0) { return i; } } } return -1; } private int addAppWindowToListLocked(final WindowState win) { final DisplayContent displayContent = win.getDisplayContent(); if (displayContent == null) { // It doesn't matter this display is going away. return 0; } final IWindow client = win.mClient; final WindowToken token = win.mToken; final WindowList windows = displayContent.getWindowList(); WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); int tokenWindowsPos = 0; if (!tokenWindowList.isEmpty()) { return addAppWindowToTokenListLocked(win, token, windows, tokenWindowList); } // No windows from this token on this display if (localLOGV) Slog.v(TAG_WM, "Figuring out where to add app window " + client.asBinder() + " (token=" + token + ")"); // Figure out where the window should go, based on the // order of applications. WindowState pos = null; final ArrayList tasks = displayContent.getTasks(); int taskNdx; int tokenNdx = -1; for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { AppTokenList tokens = tasks.get(taskNdx).mAppTokens; for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { final AppWindowToken t = tokens.get(tokenNdx); if (t == token) { --tokenNdx; if (tokenNdx < 0) { --taskNdx; if (taskNdx >= 0) { tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1; } } break; } // We haven't reached the token yet; if this token // is not going to the bottom and has windows on this display, we can // use it as an anchor for when we do reach the token. tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); if (!t.sendingToBottom && tokenWindowList.size() > 0) { pos = tokenWindowList.get(0); } } if (tokenNdx >= 0) { // early exit break; } } // We now know the index into the apps. If we found // an app window above, that gives us the position; else // we need to look some more. if (pos != null) { // Move behind any windows attached to this one. WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); if (atoken != null) { tokenWindowList = getTokenWindowsOnDisplay(atoken, displayContent); final int NC = tokenWindowList.size(); if (NC > 0) { WindowState bottom = tokenWindowList.get(0); if (bottom.mSubLayer < 0) { pos = bottom; } } } placeWindowBefore(pos, win); return tokenWindowsPos; } // Continue looking down until we find the first // token that has windows on this display. for ( ; taskNdx >= 0; --taskNdx) { AppTokenList tokens = tasks.get(taskNdx).mAppTokens; for ( ; tokenNdx >= 0; --tokenNdx) { final AppWindowToken t = tokens.get(tokenNdx); tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); final int NW = tokenWindowList.size(); if (NW > 0) { pos = tokenWindowList.get(NW-1); break; } } if (tokenNdx >= 0) { // found break; } } if (pos != null) { // Move in front of any windows attached to this // one. WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); if (atoken != null) { final int NC = atoken.windows.size(); if (NC > 0) { WindowState top = atoken.windows.get(NC-1); if (top.mSubLayer >= 0) { pos = top; } } } placeWindowAfter(pos, win); return tokenWindowsPos; } // Just search for the start of this layer. final int myLayer = win.mBaseLayer; int i; for (i = windows.size() - 1; i >= 0; --i) { WindowState w = windows.get(i); // Dock divider shares the base layer with application windows, but we want to always // keep it above the application windows. The sharing of the base layer is intended // for window animations, which need to be above the dock divider for the duration // of the animation. if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) { break; } } if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + windows.size()); windows.add(i + 1, win); mWindowsChanged = true; return tokenWindowsPos; } private int addAppWindowToTokenListLocked(WindowState win, WindowToken token, WindowList windows, WindowList tokenWindowList) { int tokenWindowsPos; // If this application has existing windows, we // simply place the new window on top of them... but // keep the starting window on top. if (win.mAttrs.type == TYPE_BASE_APPLICATION) { // Base windows go behind everything else. WindowState lowestWindow = tokenWindowList.get(0); placeWindowBefore(lowestWindow, win); tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows); } else { AppWindowToken atoken = win.mAppToken; final int windowListPos = tokenWindowList.size(); WindowState lastWindow = tokenWindowList.get(windowListPos - 1); if (atoken != null && lastWindow == atoken.startingWindow) { placeWindowBefore(lastWindow, win); tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows); } else { int newIdx = findIdxBasedOnAppTokens(win); //there is a window above this one associated with the same //apptoken note that the window could be a floating window //that was created later or a window at the top of the list of //windows associated with this token. if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " + windows.size()); windows.add(newIdx + 1, win); if (newIdx < 0) { // No window from token found on win's display. tokenWindowsPos = 0; } else { tokenWindowsPos = indexOfWinInWindowList( windows.get(newIdx), token.windows) + 1; } mWindowsChanged = true; } } return tokenWindowsPos; } private void addFreeWindowToListLocked(final WindowState win) { final WindowList windows = win.getWindowList(); // Figure out where window should go, based on layer. final int myLayer = win.mBaseLayer; int i; for (i = windows.size() - 1; i >= 0; i--) { final WindowState otherWin = windows.get(i); if (otherWin.getBaseType() != TYPE_WALLPAPER && otherWin.mBaseLayer <= myLayer) { // Wallpaper wanders through the window list, for example to position itself // directly behind keyguard. Because of this it will break the ordering based on // WindowState.mBaseLayer. There might windows with higher mBaseLayer behind it and // we don't want the new window to appear above them. An example of this is adding // of the docked stack divider. Consider a scenario with the following ordering (top // to bottom): keyguard, wallpaper, assist preview, apps. We want the dock divider // to land below the assist preview, so the dock divider must ignore the wallpaper, // with which it shares the base layer. break; } } i++; if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Free window: Adding window " + win + " at " + i + " of " + windows.size()); windows.add(i, win); mWindowsChanged = true; } private void addAttachedWindowToListLocked(final WindowState win, boolean addToToken) { final WindowToken token = win.mToken; final DisplayContent displayContent = win.getDisplayContent(); if (displayContent == null) { return; } final WindowState attached = win.mAttachedWindow; WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); // Figure out this window's ordering relative to the window // it is attached to. final int NA = tokenWindowList.size(); final int sublayer = win.mSubLayer; int largestSublayer = Integer.MIN_VALUE; WindowState windowWithLargestSublayer = null; int i; for (i = 0; i < NA; i++) { WindowState w = tokenWindowList.get(i); final int wSublayer = w.mSubLayer; if (wSublayer >= largestSublayer) { largestSublayer = wSublayer; windowWithLargestSublayer = w; } if (sublayer < 0) { // For negative sublayers, we go below all windows // in the same sublayer. if (wSublayer >= sublayer) { if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + token); token.windows.add(i, win); } placeWindowBefore(wSublayer >= 0 ? attached : w, win); break; } } else { // For positive sublayers, we go above all windows // in the same sublayer. if (wSublayer > sublayer) { if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + token); token.windows.add(i, win); } placeWindowBefore(w, win); break; } } } if (i >= NA) { if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + token); token.windows.add(win); } if (sublayer < 0) { placeWindowBefore(attached, win); } else { placeWindowAfter(largestSublayer >= 0 ? windowWithLargestSublayer : attached, win); } } } private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) { if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win + " Callers=" + Debug.getCallers(4)); if (win.mAttachedWindow == null) { final WindowToken token = win.mToken; int tokenWindowsPos = 0; if (token.appWindowToken != null) { tokenWindowsPos = addAppWindowToListLocked(win); } else { addFreeWindowToListLocked(win); } if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + token); token.windows.add(tokenWindowsPos, win); } } else { addAttachedWindowToListLocked(win, addToToken); } final AppWindowToken appToken = win.mAppToken; if (appToken != null) { if (addToToken) { appToken.addWindow(win); } } } static boolean canBeImeTarget(WindowState w) { final int fl = w.mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); final int type = w.mAttrs.type; if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) || type == TYPE_APPLICATION_STARTING) { if (DEBUG_INPUT_METHOD) { Slog.i(TAG_WM, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding()); if (!w.isVisibleOrAdding()) { Slog.i(TAG_WM, " mSurfaceController=" + w.mWinAnimator.mSurfaceController + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility + " policyVis=" + w.mPolicyVisibility + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim + " attachHid=" + w.mAttachedHidden + " exiting=" + w.mAnimatingExit + " destroying=" + w.mDestroying); if (w.mAppToken != null) { Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + w.mAppToken.hiddenRequested); } } } return w.isVisibleOrAdding(); } return false; } /** * Dig through the WindowStates and find the one that the Input Method will target. * @param willMove * @return The index+1 in mWindows of the discovered target. */ int findDesiredInputMethodWindowIndexLocked(boolean willMove) { // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the // same display. Or even when the current IME/target are not on the same screen as the next // IME/target. For now only look for input windows on the main screen. WindowList windows = getDefaultWindowListLocked(); WindowState w = null; int i; for (i = windows.size() - 1; i >= 0; --i) { WindowState win = windows.get(i); if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG_WM, "Checking window @" + i + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags)); if (canBeImeTarget(win)) { w = win; //Slog.i(TAG_WM, "Putting input method here!"); // Yet more tricksyness! If this window is a "starting" // window, we do actually want to be on top of it, but // it is not -really- where input will go. So if the caller // is not actually looking to move the IME, look down below // for a real window to target... if (!willMove && w.mAttrs.type == TYPE_APPLICATION_STARTING && i > 0) { WindowState wb = windows.get(i-1); if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { i--; w = wb; } } break; } } // Now w is either mWindows[0] or an IME (or null if mWindows is empty). if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG_WM, "Proposed new IME target: " + w); // Now, a special case -- if the last target's window is in the // process of exiting, and is above the new target, keep on the // last target to avoid flicker. Consider for example a Dialog with // the IME shown: when the Dialog is dismissed, we want to keep // the IME above it until it is completely gone so it doesn't drop // behind the dialog or its full-screen scrim. final WindowState curTarget = mInputMethodTarget; if (curTarget != null && curTarget.isDisplayedLw() && curTarget.isClosing() && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) { if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Current target higher, not changing"); return windows.indexOf(curTarget) + 1; } if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target=" + w + " willMove=" + willMove); if (willMove && w != null) { AppWindowToken token = curTarget == null ? null : curTarget.mAppToken; if (token != null) { // Now some fun for dealing with window animations that // modify the Z order. We need to look at all windows below // the current target that are in this app, finding the highest // visible one in layering. WindowState highestTarget = null; int highestPos = 0; if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) { WindowList curWindows = curTarget.getWindowList(); int pos = curWindows.indexOf(curTarget); while (pos >= 0) { WindowState win = curWindows.get(pos); if (win.mAppToken != token) { break; } if (!win.mRemoved) { if (highestTarget == null || win.mWinAnimator.mAnimLayer > highestTarget.mWinAnimator.mAnimLayer) { highestTarget = win; highestPos = pos; } } pos--; } } if (highestTarget != null) { if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget + " animating=" + highestTarget.mWinAnimator.isAnimationSet() + " layer=" + highestTarget.mWinAnimator.mAnimLayer + " new layer=" + w.mWinAnimator.mAnimLayer); if (mAppTransition.isTransitionSet()) { // If we are currently setting up for an animation, // hold everything until we can find out what will happen. mInputMethodTargetWaitingAnim = true; mInputMethodTarget = highestTarget; return highestPos + 1; } else if (highestTarget.mWinAnimator.isAnimationSet() && highestTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) { // If the window we are currently targeting is involved // with an animation, and it is on top of the next target // we will be over, then hold off on moving until // that is done. mInputMethodTargetWaitingAnim = true; mInputMethodTarget = highestTarget; return highestPos + 1; } } } } //Slog.i(TAG_WM, "Placing input method @" + (i+1)); if (w != null) { if (willMove) { if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to " + w + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : "")); mInputMethodTarget = w; mInputMethodTargetWaitingAnim = false; if (w.mAppToken != null) { mLayersController.setInputMethodAnimLayerAdjustment( w.mAppToken.mAppAnimator.animLayerAdjustment); } else { mLayersController.setInputMethodAnimLayerAdjustment(0); } } // If the docked divider is visible, we still need to go through this whole // excercise to find the appropriate input method target (used for animations // and dialog adjustments), but for purposes of Z ordering we simply wish to // place it above the docked divider. Unless it is already above the divider. WindowState dockedDivider = w.mDisplayContent.mDividerControllerLocked.getWindow(); if (dockedDivider != null && dockedDivider.isVisibleLw()) { int dividerIndex = windows.indexOf(dockedDivider); if (dividerIndex > 0 && dividerIndex > i) { return dividerIndex + 1; } } return i+1; } if (willMove) { if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to null." + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : "")); mInputMethodTarget = null; mLayersController.setInputMethodAnimLayerAdjustment(0); } return -1; } void addInputMethodWindowToListLocked(WindowState win) { int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { win.mTargetAppToken = mInputMethodTarget.mAppToken; if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG_WM, "Adding input method window " + win + " at " + pos); // TODO(multidisplay): IMEs are only supported on the default display. getDefaultWindowListLocked().add(pos, win); mWindowsChanged = true; moveInputMethodDialogsLocked(pos + 1); return; } win.mTargetAppToken = null; addWindowToListInOrderLocked(win, true); moveInputMethodDialogsLocked(pos); } private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { WindowList windows = win.getWindowList(); int wpos = windows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + win); windows.remove(wpos); mWindowsChanged = true; int NC = win.mChildWindows.size(); while (NC > 0) { NC--; WindowState cw = win.mChildWindows.get(NC); int cpos = windows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing child at " + cpos + ": " + cw); windows.remove(cpos); } } } return interestingPos; } private void reAddWindowToListInOrderLocked(WindowState win) { addWindowToListInOrderLocked(win, false); // This is a hack to get all of the child windows added as well // at the right position. Child windows should be rare and // this case should be rare, so it shouldn't be that big a deal. WindowList windows = win.getWindowList(); int wpos = windows.indexOf(win); if (wpos >= 0) { if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "ReAdd removing from " + wpos + ": " + win); windows.remove(wpos); mWindowsChanged = true; reAddWindowLocked(wpos, win); } } void logWindowList(final WindowList windows, String prefix) { int N = windows.size(); while (N > 0) { N--; Slog.v(TAG_WM, prefix + "#" + N + ": " + windows.get(N)); } } void moveInputMethodDialogsLocked(int pos) { ArrayList dialogs = mInputMethodDialogs; // TODO(multidisplay): IMEs are only supported on the default display. WindowList windows = getDefaultWindowListLocked(); final int N = dialogs.size(); if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; // Skip windows owned by the input method. if (mInputMethodWindow != null) { while (pos < windows.size()) { WindowState wp = windows.get(pos); if (wp == mInputMethodWindow || wp.mAttachedWindow == mInputMethodWindow) { pos++; continue; } break; } } if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Adding " + N + " dialogs at pos=" + pos); for (int i=0; i= 0) { // In this case, the input method windows are to be placed // immediately above the window they are targeting. // First check to see if the input method windows are already // located here, and contiguous. final int N = windows.size(); WindowState firstImWin = imPos < N ? windows.get(imPos) : null; // Figure out the actual input method window that should be // at the bottom of their stack. WindowState baseImWin = imWin != null ? imWin : mInputMethodDialogs.get(0); if (baseImWin.mChildWindows.size() > 0) { WindowState cw = baseImWin.mChildWindows.get(0); if (cw.mSubLayer < 0) baseImWin = cw; } if (firstImWin == baseImWin) { // The windows haven't moved... but are they still contiguous? // First find the top IM window. int pos = imPos+1; while (pos < N) { if (!(windows.get(pos)).mIsImWindow) { break; } pos++; } pos++; // Now there should be no more input method windows above. while (pos < N) { if ((windows.get(pos)).mIsImWindow) { break; } pos++; } if (pos >= N) { // Z order is good. // The IM target window may be changed, so update the mTargetAppToken. if (imWin != null) { imWin.mTargetAppToken = mInputMethodTarget.mAppToken; } return false; } } if (imWin != null) { if (DEBUG_INPUT_METHOD) { Slog.v(TAG_WM, "Moving IM from " + imPos); logWindowList(windows, " "); } imPos = tmpRemoveWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":"); logWindowList(windows, " "); } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG_WM, "List after moving IM to " + imPos + ":"); logWindowList(windows, " "); } if (DN > 0) moveInputMethodDialogsLocked(imPos+1); } else { moveInputMethodDialogsLocked(imPos); } } else { // In this case, the input method windows go in a fixed layer, // because they aren't currently associated with a focus window. if (imWin != null) { if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); imWin.mTargetAppToken = null; reAddWindowToListInOrderLocked(imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG_WM, "List with no IM target:"); logWindowList(windows, " "); } if (DN > 0) moveInputMethodDialogsLocked(-1); } else { moveInputMethodDialogsLocked(-1); } } if (needAssignLayers) { mLayersController.assignLayersLocked(windows); } return true; } private 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, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { int[] appOp = new int[1]; int res = mPolicy.checkAddPermission(attrs, appOp); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } boolean reportNewConfig = false; WindowState attachedWindow = null; long origId; final int type = attrs.type; synchronized(mWindowMap) { if (!mDisplayReady) { throw new IllegalStateException("Display has not been initialialized"); } final DisplayContent displayContent = getDisplayContentLocked(displayId); 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) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == 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 (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.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; } boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); AppWindowToken atoken = null; if (token == null) { if (type >= FIRST_APPLICATION_WINDOW && type <= 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 (type == 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 (type == 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 (type == TYPE_WALLPAPER) { Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_DREAM) { Slog.w(TAG_WM, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == 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 (type == 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; } token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { atoken = token.appWindowToken; 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; } if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v( TAG_WM, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; } } else if (type == 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 (type == 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 (type == 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 (type == 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 (type == 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_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.appWindowToken != null) { Slog.w(TAG_WM, "Non-null appWindowToken for system window of type=" + type); // 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, null, -1, false); addToken = true; } WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); 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; } mPolicy.adjustWindowParamsLw(win.mAttrs); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); res = mPolicy.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); } // From now on, no exceptions or errors allowed! res = WindowManagerGlobal.ADD_OKAY; if (excludeWindowTypeFromTapOutTask(type)) { displayContent.mTapExcludedWindows.add(win); } origId = Binder.clearCallingIdentity(); if (addToken) { mTokenMap.put(attrs.token, token); } win.attach(); mWindowMap.put(client.asBinder(), win); if (win.mAppOp != AppOpsManager.OP_NONE) { int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); if ((startOpResult != AppOpsManager.MODE_ALLOWED) && (startOpResult != AppOpsManager.MODE_DEFAULT)) { win.setAppOpVisibilityLw(false); } } if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + token.appWindowToken + " startingWindow=" + win); } boolean imMayMove = true; if (type == TYPE_INPUT_METHOD) { win.mGivenInsetsPending = true; mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (type == TYPE_WALLPAPER) { mWallpaperControllerLocked.clearLastWallpaperTimeoutTime(); displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if (mWallpaperControllerLocked.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 task that's docked but non-resizeable, // we need to update this new window's scroll position when it's added. win.applyScrollIfNeeded(); // 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) { getDefaultDisplayContentLocked().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 && !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); } if (displayContent.isDefaultDisplay) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final Rect taskBounds; if (atoken != null && atoken.mTask != null) { taskBounds = mTmpRect; atoken.mTask.getBounds(mTmpRect); } else { taskBounds = null; } if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, mRotation, displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets, outStableInsets, outOutsets)) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR; } } else { outContentInsets.setEmpty(); outStableInsets.setEmpty(); } if (mInTouchMode) { res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; } if (win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; } mInputMonitor.setUpdateInputWindowsNeededLw(); boolean focusChanged = false; if (win.canReceiveKeys()) { focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, false /*updateInputWindows*/); if (focusChanged) { imMayMove = false; } } if (imMayMove) { moveInputMethodWindowsIfNeededLocked(false); } mLayersController.assignLayersLocked(displayContent.getWindowList()); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. if (focusChanged) { mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/); } mInputMonitor.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() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; } if (attrs.removeTimeoutMilliseconds > 0) { mH.sendMessageDelayed( mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win), attrs.removeTimeoutMilliseconds); } } if (reportNewConfig) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return res; } /** * Returns true if we're done setting up any transitions. */ private boolean prepareWindowReplacementTransition(AppWindowToken atoken) { atoken.clearAllDrawn(); WindowState replacedWindow = null; for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) { WindowState candidate = atoken.windows.get(i); if (candidate.mAnimatingExit && candidate.mWillReplaceWindow && candidate.mAnimateReplacingWindow) { replacedWindow = candidate; } } 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.mVisibleFrame; // 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. mOpeningApps.add(atoken); prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT); mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, frame.width(), frame.height()); 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. if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) { mOpeningApps.add(atoken); prepareAppTransition(AppTransition.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT); executeAppTransition(); } } /** * Returns whether screen capture is disabled for all windows of a specific user. */ boolean isScreenCaptureDisabledLocked(int userId) { Boolean disabled = mScreenCaptureDisabled.get(userId); if (disabled == null) { return false; } return disabled; } boolean isSecureLocked(WindowState w) { if ((w.mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { return true; } if (isScreenCaptureDisabledLocked(UserHandle.getUserId(w.mOwnerUid))) { return true; } return false; } /** * Set mScreenCaptureDisabled for specific user */ @Override public void setScreenCaptureDisabled(int userId, boolean disabled) { int callingUid = Binder.getCallingUid(); if (callingUid != Process.SYSTEM_UID) { throw new SecurityException("Only system can call setScreenCaptureDisabled."); } synchronized(mWindowMap) { mScreenCaptureDisabled.put(userId, disabled); // Update secure surface for all windows belonging to this user. for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) { WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { final WindowState win = windows.get(winNdx); if (win.mHasSurface && userId == UserHandle.getUserId(win.mOwnerUid)) { win.mWinAnimator.setSecureLocked(disabled); } } } } } private void setupWindowForRemoveOnExit(WindowState win) { win.mRemoveOnExit = true; win.setDisplayLayoutNeeded(); // Request a focus update as this window's input channel is already gone. Otherwise // we could have no focused window in input manager. final boolean focusChanged = updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); mWindowPlacerLocked.performSurfacePlacement(); if (focusChanged) { mInputMonitor.updateInputWindowsLw(false /*force*/); } } public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } removeWindowLocked(win); } } void removeWindowLocked(WindowState win) { removeWindowLocked(win, false); } void removeWindowLocked(WindowState win, boolean keepVisibleDeadWindow) { win.mWindowRemovalAllowed = true; if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowLocked: " + win + " callers=" + Debug.getCallers(4)); final boolean startingWindow = win.mAttrs.type == TYPE_APPLICATION_STARTING; if (startingWindow) { if (DEBUG_STARTING_WINDOW) Slog.d(TAG_WM, "Starting window removed " + win); } if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win == mCurrentFocus) Slog.v( TAG_WM, "Remove " + win + " client=" + Integer.toHexString(System.identityHashCode(win.mClient.asBinder())) + ", surfaceController=" + win.mWinAnimator.mSurfaceController + " Callers=" + Debug.getCallers(4)); final long origId = Binder.clearCallingIdentity(); win.disposeInputChannel(); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + win + ": mSurfaceController=" + win.mWinAnimator.mSurfaceController + " mAnimatingExit=" + win.mAnimatingExit + " mRemoveOnExit=" + win.mRemoveOnExit + " mHasSurface=" + win.mHasSurface + " surfaceShowing=" + win.mWinAnimator.getShown() + " isAnimationSet=" + win.mWinAnimator.isAnimationSet() + " app-animation=" + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null) + " mWillReplaceWindow=" + win.mWillReplaceWindow + " inPendingTransaction=" + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false) + " mDisplayFrozen=" + mDisplayFrozen + " callers=" + Debug.getCallers(6)); // Visibility of the removed window. Will be used later to update orientation later on. boolean wasVisible = false; // First, see if we need to run an animation. If we do, we have to hold off on removing the // window until the animation is done. If the display is frozen, just remove immediately, // since the animation wouldn't be seen. if (win.mHasSurface && okToDisplay()) { final AppWindowToken appToken = win.mAppToken; if (win.mWillReplaceWindow) { // This window is going to be replaced. We need to keep it around until the new one // gets added, then we will get rid of this one. if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Preserving " + win + " until the new one is " + "added"); // TODO: We are overloading mAnimatingExit flag to prevent the window state from // been removed. We probably need another flag to indicate that window removal // should be deffered vs. overloading the flag that says we are playing an exit // animation. win.mAnimatingExit = true; win.mReplacingRemoveRequested = true; Binder.restoreCallingIdentity(origId); return; } if (win.isAnimatingWithSavedSurface() && !appToken.allDrawnExcludingSaved) { // We started enter animation early with a saved surface, now the app asks to remove // this window. If we remove it now and the app is not yet drawn, we'll show a // flicker. Delay the removal now until it's really drawn. if (DEBUG_ADD_REMOVE) { Slog.d(TAG_WM, "removeWindowLocked: delay removal of " + win + " due to early animation"); } // Do not set mAnimatingExit to true here, it will cause the surface to be hidden // immediately after the enter animation is done. If the app is not yet drawn then // it will show up as a flicker. setupWindowForRemoveOnExit(win); Binder.restoreCallingIdentity(origId); return; } // If we are not currently running the exit animation, we need to see about starting one wasVisible = win.isWinVisibleLw(); if (keepVisibleDeadWindow) { if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Not removing " + win + " because app died while it's visible"); win.mAppDied = true; win.setDisplayLayoutNeeded(); mWindowPlacerLocked.performSurfacePlacement(); // Set up a replacement input channel since the app is now dead. // We need to catch tapping on the dead window to restart the app. win.openInputChannel(null); mInputMonitor.updateInputWindowsLw(true /*force*/); Binder.restoreCallingIdentity(origId); return; } final WindowStateAnimator winAnimator = win.mWinAnimator; if (wasVisible) { final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE; // Try starting an animation. if (winAnimator.applyAnimationLocked(transit, false)) { win.mAnimatingExit = true; } //TODO (multidisplay): Magnification is supported only for the default display. if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, transit); } } final boolean isAnimating = winAnimator.isAnimationSet() && !winAnimator.isDummyAnimation(); final boolean lastWindowIsStartingWindow = startingWindow && appToken != null && appToken.allAppWindows.size() == 1; // We delay the removal of a window if it has a showing surface that can be used to run // exit animation and it is marked as exiting. // Also, If isn't the an animating starting window that is the last window in the app. // We allow the removal of the non-animating starting window now as there is no // additional window or animation that will trigger its removal. if (winAnimator.getShown() && win.mAnimatingExit && (!lastWindowIsStartingWindow || isAnimating)) { // The exit animation is running or should run... wait for it! if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Not removing " + win + " due to exit animation "); setupWindowForRemoveOnExit(win); if (appToken != null) { appToken.updateReportedVisibilityLocked(); } Binder.restoreCallingIdentity(origId); return; } } removeWindowInnerLocked(win); // Removing a visible window will effect the computed orientation // So just update orientation if needed. if (wasVisible && updateOrientationFromAppTokensLocked(false)) { mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); Binder.restoreCallingIdentity(origId); } void removeWindowInnerLocked(WindowState win) { if (win.mRemoved) { // Nothing to do. if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "removeWindowInnerLocked: " + win + " Already removed..."); return; } for (int i = win.mChildWindows.size() - 1; i >= 0; i--) { WindowState cwin = win.mChildWindows.get(i); Slog.w(TAG_WM, "Force-removing child win " + cwin + " from container " + win); removeWindowInnerLocked(cwin); } win.mRemoved = true; if (mInputMethodTarget == win) { moveInputMethodWindowsIfNeededLocked(false); } if (false) { RuntimeException e = new RuntimeException("here"); e.fillInStackTrace(); Slog.w(TAG_WM, "Removing window " + win, e); } final int type = win.mAttrs.type; if (excludeWindowTypeFromTapOutTask(type)) { final DisplayContent displaycontent = win.getDisplayContent(); displaycontent.mTapExcludedWindows.remove(win); } mPolicy.removeWindowLw(win); win.removeLocked(); if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "removeWindowInnerLocked: " + win); mWindowMap.remove(win.mClient.asBinder()); if (win.mAppOp != AppOpsManager.OP_NONE) { mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); } mPendingRemove.remove(win); mResizingWindows.remove(win); mWindowsChanged = true; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Final remove of window: " + win); if (mInputMethodWindow == win) { mInputMethodWindow = null; } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.remove(win); } final WindowToken token = win.mToken; final AppWindowToken atoken = win.mAppToken; if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing " + win + " from " + token); token.windows.remove(win); if (atoken != null) { atoken.allAppWindows.remove(win); } if (localLOGV) Slog.v( TAG_WM, "**** Removing window " + win + ": count=" + token.windows.size()); if (token.windows.size() == 0) { if (!token.explicit) { mTokenMap.remove(token.token); } else if (atoken != null) { atoken.firstWindowDrawn = false; atoken.clearAllDrawn(); } } if (atoken != null) { if (atoken.startingWindow == win) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win); scheduleRemoveStartingWindowLocked(atoken); } else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) { // If this is the last window and we had requested a starting // transition window, well there is no point now. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow"); atoken.startingData = null; } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) { // If this is the last window except for a starting transition // window, we need to get rid of the starting transition. scheduleRemoveStartingWindowLocked(atoken); } } if (type == TYPE_WALLPAPER) { mWallpaperControllerLocked.clearLastWallpaperTimeoutTime(); getDefaultDisplayContentLocked().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { getDefaultDisplayContentLocked().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } final WindowList windows = win.getWindowList(); if (windows != null) { windows.remove(win); if (!mWindowPlacerLocked.isInLayout()) { mLayersController.assignLayersLocked(windows); win.setDisplayLayoutNeeded(); mWindowPlacerLocked.performSurfacePlacement(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } } } mInputMonitor.updateInputWindowsLw(true /*force*/); } public void updateAppOpsState() { synchronized(mWindowMap) { final int numDisplays = mDisplayContents.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); final int numWindows = windows.size(); for (int winNdx = 0; winNdx < numWindows; ++winNdx) { final WindowState win = windows.get(winNdx); if (win.mAppOp != AppOpsManager.OP_NONE) { final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_DEFAULT); } } } } } 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 (mWindowMap) { 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 (mWindowMap) { 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(); } } } finally { Binder.restoreCallingIdentity(origId); } } public void getWindowDisplayFrame(Session session, IWindow client, Rect outDisplayFrame) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { outDisplayFrame.setEmpty(); return; } outDisplayFrame.set(win.mDisplayFrame); } } public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) { synchronized (mWindowMap) { if (mAccessibilityController != null) { WindowState window = mWindowMap.get(token); //TODO (multidisplay): Magnification is supported only for the default display. if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle); } } } } public IWindowId getWindowId(IBinder token) { synchronized (mWindowMap) { WindowState window = mWindowMap.get(token); return window != null ? window.mWindowId : null; } } public void pokeDrawLock(Session session, IBinder token) { synchronized (mWindowMap) { WindowState window = windowForClientLocked(session, token, false); if (window != null) { window.pokeDrawLockLw(mDrawLockTimeoutMillis); } } } void repositionChild(Session session, IWindow client, int left, int top, int right, int bottom, long frameNumber, Rect outFrame) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild"); long origId = Binder.clearCallingIdentity(); try { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } if (win.mAttachedWindow == null) { throw new IllegalArgumentException( "repositionChild called but window is not" + "attached to a parent win=" + win); } win.mAttrs.x = left; win.mAttrs.y = top; win.mAttrs.width = right - left; win.mAttrs.height = bottom - top; win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight); if (win.mHasSurface) { if (SHOW_TRANSACTIONS) { Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild"); } SurfaceControl.openTransaction(); try { win.applyGravityAndUpdateFrame(win.mContainingFrame, win.mDisplayFrame); win.mWinAnimator.computeShownFrameLocked(); win.mWinAnimator.setSurfaceBoundariesLocked(false); if (frameNumber > 0) { win.mWinAnimator.deferTransactionUntilParentFrame(frameNumber); } } finally { SurfaceControl.closeTransaction(); if (SHOW_TRANSACTIONS) { Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild"); } } } outFrame = win.mCompatFrame; } } finally { Binder.restoreCallingIdentity(origId); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } public int relayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame, Configuration outConfig, Surface outSurface) { int result = 0; boolean configChanged; boolean hasStatusBarPermission = mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) == PackageManager.PERMISSION_GRANTED; long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } WindowStateAnimator winAnimator = win.mWinAnimator; if (viewVisibility != View.GONE) { win.setRequestedSize(requestedWidth, requestedHeight); } int attrChanges = 0; int flagChanges = 0; if (attrs != null) { mPolicy.adjustWindowParamsLw(attrs); // 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) { 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 (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0; win.mEnforceSizeCompat = (win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 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); } boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0; final boolean isDefaultDisplay = win.isDefaultDisplay(); boolean focusMayChange = isDefaultDisplay && (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; final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; if (DEBUG_SCREEN_ON) { RuntimeException stack = new RuntimeException(); stack.fillInStackTrace(); Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility + " newVis=" + viewVisibility, stack); } if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { result = relayoutVisibleWindow(outConfig, result, win, winAnimator, attrChanges, oldVisibility); try { result = createSurfaceControl(outSurface, result, win, winAnimator); } catch (Exception e) { mInputMonitor.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 = isDefaultDisplay; } if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) { mInputMethodWindow = win; imMayMove = true; } win.adjustStartingWindowFlags(); } else { winAnimator.mEnterAnimationPending = false; winAnimator.mEnteringAnimation = false; final boolean usingSavedSurfaceBeforeVisible = oldVisibility != View.VISIBLE && win.isAnimatingWithSavedSurface(); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { if (winAnimator.hasSurface() && !win.mAnimatingExit && usingSavedSurfaceBeforeVisible) { Slog.d(TAG, "Ignoring layout to invisible when using saved surface " + win); } } if (winAnimator.hasSurface() && !win.mAnimatingExit && !usingSavedSurfaceBeforeVisible) { if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit); // 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. if (!win.mWillReplaceWindow) { focusMayChange = tryStartExitingAnimation( win, winAnimator, isDefaultDisplay, focusMayChange); } result |= RELAYOUT_RES_SURFACE_CHANGED; } outSurface.release(); if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); } if (focusMayChange) { //System.out.println("Focus may change: " + win.mAttrs.getTitle()); if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/)) { imMayMove = false; } //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); } // 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 && (moveInputMethodWindowsIfNeededLocked(false) || 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. mLayersController.assignLayersLocked(win.getWindowList()); } if (wallpaperMayMove) { getDefaultDisplayContentLocked().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } win.setDisplayLayoutNeeded(); win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; configChanged = updateOrientationFromAppTokensLocked(false); mWindowPlacerLocked.performSurfacePlacement(); if (toBeDisplayed && win.mIsWallpaper) { DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); mWallpaperControllerLocked.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 (mPolicy.isNavBarForcedShownLw(win)) { result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR; } if (!win.isGoneForLayoutLw()) { win.mResizedWhileGone = false; } outFrame.set(win.mCompatFrame); outOverscanInsets.set(win.mOverscanInsets); outContentInsets.set(win.mContentInsets); outVisibleInsets.set(win.mVisibleInsets); outStableInsets.set(win.mStableInsets); outOutsets.set(win.mOutsets); outBackdropFrame.set(win.getBackdropFrame(win.mFrame)); if (localLOGV) Slog.v( TAG_WM, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth + ", requestedHeight=" + requestedHeight + ", viewVisibility=" + viewVisibility + "\nRelayout returning frame=" + outFrame + ", surface=" + outSurface); if (localLOGV || DEBUG_FOCUS) Slog.v( TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange); result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0; mInputMonitor.updateInputWindowsLw(true /*force*/); if (DEBUG_LAYOUT) { Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString()); } win.mInRelayout = false; } if (configChanged) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return result; } private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator, boolean isDefaultDisplay, 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 = isDefaultDisplay; win.mAnimatingExit = true; win.mWinAnimator.mAnimating = true; } else if (win.mWinAnimator.isAnimationSet()) { // Currently in a hide animation... turn this into // an exit. win.mAnimatingExit = true; win.mWinAnimator.mAnimating = true; } else if (mWallpaperControllerLocked.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; win.mWinAnimator.mAnimating = true; } else { if (mInputMethodWindow == win) { mInputMethodWindow = null; } win.destroyOrSaveSurface(); } //TODO (multidisplay): Magnification is supported only for the default if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, transit); } return focusMayChange; } private int createSurfaceControl(Surface outSurface, int result, WindowState win, WindowStateAnimator winAnimator) { if (!win.mHasSurface) { result |= RELAYOUT_RES_SURFACE_CHANGED; } WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked(); if (surfaceController != null) { surfaceController.getSurface(outSurface); if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurface + ": copied"); } else { // For some reason there isn't a surface. Clear the // caller's object so they see the same state. outSurface.release(); } return result; } private int relayoutVisibleWindow(Configuration outConfig, int result, WindowState win, WindowStateAnimator winAnimator, int attrChanges, int oldVisibility) { result |= !win.isVisibleLw() ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0; if (win.mAnimatingExit) { Slog.d(TAG, "relayoutVisibleWindow: " + win + " mAnimatingExit=true, mRemoveOnExit=" + win.mRemoveOnExit + ", mDestroying=" + win.mDestroying); winAnimator.cancelExitAnimationForNextAnimationLocked(); win.mAnimatingExit = false; } if (win.mDestroying) { win.mDestroying = false; mDestroySurface.remove(win); } if (oldVisibility == View.GONE) { winAnimator.mEnterAnimationPending = true; } winAnimator.mEnteringAnimation = true; if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { win.prepareWindowToDisplayDuringRelayout(outConfig); } if ((attrChanges & LayoutParams.FORMAT_CHANGED) != 0) { // If the format can't be changed in place, preserve the old surface until the app draws // on the new one. This prevents blinking when we change elevation of freeform and // pinned windows. if (!winAnimator.tryChangeFormatInPlaceLocked()) { winAnimator.preserveSurfaceLocked(); result |= RELAYOUT_RES_SURFACE_CHANGED | WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; } } // If we're starting a drag-resize, we'll be changing the surface size as well as // notifying the client to render to with an offset from the surface's top-left. if (win.isDragResizeChanged() || win.isResizedWhileNotDragResizing()) { win.setDragResizing(); win.setResizedWhileNotDragResizing(false); // We can only change top level windows to the full-screen surface when // resizing (as we only have one full-screen surface). So there is no need // to preserve and destroy windows which are attached to another, they // will keep their surface and its size may change over time. if (win.mHasSurface && win.mAttachedWindow == null) { winAnimator.preserveSurfaceLocked(); result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; } } final boolean freeformResizing = win.isDragResizing() && win.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM; final boolean dockedResizing = win.isDragResizing() && win.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER; result |= freeformResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0; result |= dockedResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0; if (win.isAnimatingWithSavedSurface()) { // If we're animating with a saved surface now, request client to report draw. // We still need to know when the real thing is drawn. result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; } return result; } public void performDeferredDestroyWindow(Session session, IWindow client) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null || win.mWillReplaceWindow) { return; } win.mWinAnimator.destroyDeferredSurfaceLocked(); } } finally { Binder.restoreCallingIdentity(origId); } } public boolean outOfMemoryWindow(Session session, IWindow client) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return false; } return reclaimSomeSurfaceMemoryLocked(win.mWinAnimator, "from-client", false); } } finally { Binder.restoreCallingIdentity(origId); } } public void finishDrawingWindow(Session session, IWindow client) { final long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { 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) { getDefaultDisplayContentLocked().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } win.setDisplayLayoutNeeded(); mWindowPlacerLocked.requestTraversal(); } } } finally { Binder.restoreCallingIdentity(origId); } } private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. if (okToDisplay()) { DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); final int width = displayInfo.appWidth; final int height = displayInfo.appHeight; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG_WM, "applyAnimation: atoken=" + atoken); // Determine the visible rect to calculate the thumbnail clip final WindowState win = atoken.findMainWindow(); final Rect frame = new Rect(0, 0, width, height); final Rect displayFrame = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); final Rect insets = new Rect(); Rect surfaceInsets = null; final boolean freeform = win != null && win.inFreeformWorkspace(); if (win != null) { // Containing frame will usually cover the whole screen, including dialog windows. // For freeform workspace windows it will not cover the whole screen and it also // won't exactly match the final freeform window frame (e.g. when overlapping with // the status bar). In that case we need to use the final frame. if (freeform) { frame.set(win.mFrame); } else { frame.set(win.mContainingFrame); } surfaceInsets = win.getAttrs().surfaceInsets; insets.set(win.mContentInsets); } if (atoken.mLaunchTaskBehind) { // Differentiate the two animations. This one which is briefly on the screen // gets the !enter animation, and the other activity which remains on the // screen gets the enter animation. Both appear in the mOpeningApps set. enter = false; } if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition." + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets); Animation a = mAppTransition.loadAnimation(lp, transit, enter, mCurConfiguration.uiMode, mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets, isVoiceInteraction, freeform, atoken.mTask.mTaskId); if (a != null) { if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken); final int containingWidth = frame.width(); final int containingHeight = frame.height(); atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight, mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode()); } } else { atoken.mAppAnimator.clearAnimation(); } return atoken.mAppAnimator.animation != null; } // ------------------------------------------------------------- // Application Window Tokens // ------------------------------------------------------------- public void validateAppTokens(int stackId, List tasks) { synchronized (mWindowMap) { int t = tasks.size() - 1; if (t < 0) { Slog.w(TAG_WM, "validateAppTokens: empty task list"); return; } TaskGroup task = tasks.get(0); int taskId = task.taskId; Task targetTask = mTaskIdToTask.get(taskId); DisplayContent displayContent = targetTask.getDisplayContent(); if (displayContent == null) { Slog.w(TAG_WM, "validateAppTokens: no Display for taskId=" + taskId); return; } final ArrayList localTasks = mStackIdToStack.get(stackId).getTasks(); int taskNdx; for (taskNdx = localTasks.size() - 1; taskNdx >= 0 && t >= 0; --taskNdx, --t) { AppTokenList localTokens = localTasks.get(taskNdx).mAppTokens; task = tasks.get(t); List tokens = task.tokens; DisplayContent lastDisplayContent = displayContent; displayContent = mTaskIdToTask.get(taskId).getDisplayContent(); if (displayContent != lastDisplayContent) { Slog.w(TAG_WM, "validateAppTokens: displayContent changed in TaskGroup list!"); return; } int tokenNdx; int v; for (tokenNdx = localTokens.size() - 1, v = task.tokens.size() - 1; tokenNdx >= 0 && v >= 0; ) { final AppWindowToken atoken = localTokens.get(tokenNdx); if (atoken.removed) { --tokenNdx; continue; } if (tokens.get(v) != atoken.token) { break; } --tokenNdx; v--; } if (tokenNdx >= 0 || v >= 0) { break; } } if (taskNdx >= 0 || t >= 0) { Slog.w(TAG_WM, "validateAppTokens: Mismatch! ActivityManager=" + tasks); Slog.w(TAG_WM, "validateAppTokens: Mismatch! WindowManager=" + localTasks); Slog.w(TAG_WM, "validateAppTokens: Mismatch! Callers=" + Debug.getCallers(4)); } } } public void validateStackOrder(Integer[] remoteStackIds) { // TODO: } private boolean checkCallingPermission(String permission, String func) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == Process.myPid()) { return true; } if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { return true; } String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + permission; Slog.w(TAG_WM, msg); return false; } boolean okToDisplay() { return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn(); } AppWindowToken findAppWindowToken(IBinder token) { WindowToken wtoken = mTokenMap.get(token); if (wtoken == null) { return null; } return wtoken.appWindowToken; } @Override public void addWindowToken(IBinder token, int type) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addWindowToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { WindowToken wtoken = mTokenMap.get(token); if (wtoken != null) { Slog.w(TAG_WM, "Attempted to add existing input method token: " + token); return; } wtoken = new WindowToken(this, token, type, true); mTokenMap.put(token, wtoken); if (type == TYPE_WALLPAPER) { mWallpaperControllerLocked.addWallpaperToken(wtoken); } } } @Override public void removeWindowToken(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "removeWindowToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { DisplayContent displayContent = null; WindowToken wtoken = mTokenMap.remove(token); if (wtoken != null) { boolean delayed = false; if (!wtoken.hidden) { final int N = wtoken.windows.size(); boolean changed = false; for (int i=0; i= 0; --pos) { WindowState win = windows.get(pos); if (win.mAppToken != null) { // We hit an application window. so the orientation will be determined by the // app window. No point in continuing further. break; } if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { continue; } int req = win.mAttrs.screenOrientation; if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) { continue; } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req); if (mPolicy.isKeyguardHostWindow(win.mAttrs)) { mLastKeyguardForcedOrientation = req; } return (mLastWindowForcedOrientation = req); } mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; if (mPolicy.isKeyguardLocked()) { // The screen is locked and no top system window is requesting an orientation. // Return either the orientation of the show-when-locked app (if there is any) or // the orientation of the keyguard. No point in searching from the rest of apps. WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw(); AppWindowToken appShowWhenLocked = winShowWhenLocked == null ? null : winShowWhenLocked.mAppToken; if (appShowWhenLocked != null) { int req = appShowWhenLocked.requestedOrientation; if (req == SCREEN_ORIENTATION_BEHIND) { req = mLastKeyguardForcedOrientation; } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + appShowWhenLocked + " -- show when locked, return " + req); return req; } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No one is requesting an orientation when the screen is locked"); return mLastKeyguardForcedOrientation; } } // Top system windows are not requesting an orientation. Start searching from apps. return getAppSpecifiedOrientation(); } private int getAppSpecifiedOrientation() { int lastOrientation = SCREEN_ORIENTATION_UNSPECIFIED; boolean findingBehind = false; boolean lastFullscreen = false; DisplayContent displayContent = getDefaultDisplayContentLocked(); final ArrayList tasks = displayContent.getTasks(); final boolean inMultiWindow = isStackVisibleLocked(DOCKED_STACK_ID) || isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID); final boolean dockMinimized = getDefaultDisplayContentLocked().mDividerControllerLocked.isMinimizedDock(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { AppTokenList tokens = tasks.get(taskNdx).mAppTokens; final int firstToken = tokens.size() - 1; for (int tokenNdx = firstToken; tokenNdx >= 0; --tokenNdx) { final AppWindowToken atoken = tokens.get(tokenNdx); if (DEBUG_APP_ORIENTATION) Slog.v(TAG_WM, "Checking app orientation: " + atoken); // if we're about to tear down this window and not seek for // the behind activity, don't use it for orientation if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping " + atoken + " -- going to hide"); continue; } if (tokenNdx == firstToken) { // If we have hit a new Task, and the bottom of the previous group didn't // explicitly say to use the orientation behind it, and the last app was // full screen, then we'll stick with the user's orientation. if (lastOrientation != SCREEN_ORIENTATION_BEHIND && lastFullscreen) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + atoken + " -- end of group, return " + lastOrientation); return lastOrientation; } } // We ignore any hidden applications on the top. if (atoken.hiddenRequested) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping " + atoken + " -- hidden on top"); continue; } // No app except the home app may specify the screen orientation in multi-window, // and only if the docked stack is minimized to avoid weirdness when home task // temporarily gets moved to the front. if (inMultiWindow && (!atoken.mTask.isHomeTask() || !dockMinimized)) { continue; } if (tokenNdx == 0) { // Last token in this task. lastOrientation = atoken.requestedOrientation; } int or = atoken.requestedOrientation; // If this application is fullscreen, and didn't explicitly say // to use the orientation behind it, then just take whatever // orientation it has and ignores whatever is under it. lastFullscreen = atoken.appFullscreen; if (lastFullscreen && or != SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + atoken + " -- full screen, return " + or); return or; } // If this application has requested an explicit orientation, then use it. if (or != SCREEN_ORIENTATION_UNSPECIFIED && or != SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + atoken + " -- explicitly set, return " + or); return or; } findingBehind |= (or == SCREEN_ORIENTATION_BEHIND); } } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No app is requesting an orientation, return " + mForcedAppOrientation); // The next app has not been requested to be visible, so we keep the current orientation // to prevent freezing/unfreezing the display too early unless we are in multi-window, in // which we don't let the app customize the orientation unless it was the home task that // is handled above. return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mForcedAppOrientation; } @Override public Configuration updateOrientationFromAppTokens( Configuration currentConfig, IBinder freezeThisOneIfNeeded) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } Configuration config = null; long ident = Binder.clearCallingIdentity(); synchronized(mWindowMap) { config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded); } Binder.restoreCallingIdentity(ident); return config; } private Configuration updateOrientationFromAppTokensLocked( Configuration currentConfig, IBinder freezeThisOneIfNeeded) { if (!mDisplayReady) { return null; } Configuration config = null; if (updateOrientationFromAppTokensLocked(false)) { if (freezeThisOneIfNeeded != null) { AppWindowToken atoken = findAppWindowToken(freezeThisOneIfNeeded); if (atoken != null) { startAppFreezingScreenLocked(atoken); } } config = computeNewConfigurationLocked(); } else if (currentConfig != null) { // No obvious action we need to take, but if our current // state mismatches the activity manager's, update it, // disregarding font scale, which should remain set to // the value of the previous configuration. mTempConfiguration.setToDefaults(); mTempConfiguration.updateFrom(currentConfig); computeScreenConfigurationLocked(mTempConfiguration); if (currentConfig.diff(mTempConfiguration) != 0) { mWaitingForConfig = true; final DisplayContent displayContent = getDefaultDisplayContentLocked(); displayContent.layoutNeeded = true; int anim[] = new int[2]; if (displayContent.isDimming()) { anim[0] = anim[1] = 0; } else { mPolicy.selectRotationAnimationLw(anim); } startFreezingDisplayLocked(false, anim[0], anim[1]); config = new Configuration(mTempConfiguration); } } return config; } /* * Determine the new desired orientation of the display, returning * a non-null new Configuration if it has changed from the current * orientation. IF TRUE IS RETURNED SOMEONE MUST CALL * setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE * SCREEN. This will typically be done for you if you call * sendNewConfiguration(). * * The orientation is computed from non-application windows first. If none of * the non-application windows specify orientation, the orientation is computed from * application tokens. * @see android.view.IWindowManager#updateOrientationFromAppTokens( * android.os.IBinder) */ boolean updateOrientationFromAppTokensLocked(boolean inTransaction) { long ident = Binder.clearCallingIdentity(); try { int req = getOrientationLocked(); if (req != mForcedAppOrientation) { mForcedAppOrientation = req; //send a message to Policy indicating orientation change to take //action like disabling/enabling sensors etc., mPolicy.setCurrentOrientationLw(req); if (updateRotationUncheckedLocked(inTransaction)) { // changed return true; } } return false; } finally { Binder.restoreCallingIdentity(ident); } } @Override public int[] setNewConfiguration(Configuration config) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setNewConfiguration()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { if (mWaitingForConfig) { mWaitingForConfig = false; mLastFinishedFreezeSource = "new-config"; } boolean configChanged = mCurConfiguration.diff(config) != 0; if (!configChanged) { return null; } prepareFreezingAllTaskBounds(); mCurConfiguration = new Configuration(config); return onConfigurationChanged(); } } @Override public Rect getBoundsForNewConfiguration(int stackId) { synchronized(mWindowMap) { final TaskStack stack = mStackIdToStack.get(stackId); final Rect outBounds = new Rect(); stack.getBoundsForNewConfiguration(outBounds); return outBounds; } } private void prepareFreezingAllTaskBounds() { for (int i = mDisplayContents.size() - 1; i >= 0; i--) { ArrayList stacks = mDisplayContents.valueAt(i).getStacks(); for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = stacks.get(stackNdx); stack.prepareFreezingTaskBounds(); } } } private int[] onConfigurationChanged() { mPolicy.onConfigurationChanged(); final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); if (!mReconfigureOnConfigurationChanged.contains(defaultDisplayContent)) { // The default display size information is heavily dependent on the resources in the // current configuration, so we need to reconfigure it everytime the configuration // changes. See {@link PhoneWindowManager#setInitialDisplaySize}...sigh... mReconfigureOnConfigurationChanged.add(defaultDisplayContent); } for (int i = mReconfigureOnConfigurationChanged.size() - 1; i >= 0; i--) { reconfigureDisplayLocked(mReconfigureOnConfigurationChanged.remove(i)); } defaultDisplayContent.getDockedDividerController().onConfigurationChanged(); mChangedStackList.clear(); for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) { final TaskStack stack = mStackIdToStack.valueAt(stackNdx); if (stack.onConfigurationChanged()) { mChangedStackList.add(stack.mStackId); } } return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList); } @Override public void setAppOrientation(IApplicationToken token, int requestedOrientation) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppOrientation()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { AppWindowToken atoken = findAppWindowToken(token.asBinder()); if (atoken == null) { Slog.w(TAG_WM, "Attempted to set orientation of non-existing app token: " + token); return; } atoken.requestedOrientation = requestedOrientation; } } @Override public int getAppOrientation(IApplicationToken token) { synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } return wtoken.requestedOrientation; } } void setFocusTaskRegionLocked() { if (mFocusedApp != null) { final Task task = mFocusedApp.mTask; final DisplayContent displayContent = task.getDisplayContent(); if (displayContent != null) { displayContent.setTouchExcludeRegion(task); } } } @Override public void setFocusedApp(IBinder token, boolean moveFocusNow) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setFocusedApp()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { final AppWindowToken newFocus; if (token == null) { if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, was " + mFocusedApp); newFocus = null; } else { newFocus = findAppWindowToken(token); if (newFocus == null) { Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token); } if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus + " old focus=" + mFocusedApp + " moveFocusNow=" + moveFocusNow); } final boolean changed = mFocusedApp != newFocus; if (changed) { mFocusedApp = newFocus; mInputMonitor.setFocusedAppLw(newFocus); setFocusTaskRegionLocked(); } if (moveFocusNow && changed) { final long origId = Binder.clearCallingIdentity(); updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); Binder.restoreCallingIdentity(origId); } } } /** * @param transit What kind of transition is happening. Use one of the constants * AppTransition.TRANSIT_*. * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT * be set. */ @Override public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "prepareAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { boolean prepared = mAppTransition.prepareAppTransitionLocked( transit, alwaysKeepCurrent); if (prepared && okToDisplay()) { mSkipAppTransitionAnimation = false; } } } @Override public int getPendingAppTransition() { return mAppTransition.getAppTransition(); } @Override public void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, IRemoteCallback startedCallback) { synchronized(mWindowMap) { mAppTransition.overridePendingAppTransition(packageName, enterAnim, exitAnim, startedCallback); } } @Override public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, int startHeight) { synchronized(mWindowMap) { mAppTransition.overridePendingAppTransitionScaleUp(startX, startY, startWidth, startHeight); } } @Override public void overridePendingAppTransitionClipReveal(int startX, int startY, int startWidth, int startHeight) { synchronized(mWindowMap) { mAppTransition.overridePendingAppTransitionClipReveal(startX, startY, startWidth, startHeight); } } @Override public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp) { synchronized(mWindowMap) { mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY, startedCallback, scaleUp); } } @Override public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) { synchronized(mWindowMap) { mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, startY, targetWidth, targetHeight, startedCallback, scaleUp); } } @Override public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback, boolean scaleUp) { synchronized (mWindowMap) { mAppTransition.overridePendingAppTransitionMultiThumb(specs, onAnimationStartedCallback, onAnimationFinishedCallback, scaleUp); prolongAnimationsFromSpecs(specs, scaleUp); } } void prolongAnimationsFromSpecs(@NonNull AppTransitionAnimationSpec[] specs, boolean scaleUp) { // This is used by freeform <-> recents windows transition. We need to synchronize // the animation with the appearance of the content of recents, so we will make // animation stay on the first or last frame a little longer. mTmpTaskIds.clear(); for (int i = specs.length - 1; i >= 0; i--) { mTmpTaskIds.put(specs[i].taskId, 0); } for (final WindowState win : mWindowMap.values()) { final Task task = win.getTask(); if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1 && task.inFreeformWorkspace()) { final AppWindowToken appToken = win.mAppToken; if (appToken != null && appToken.mAppAnimator != null) { appToken.mAppAnimator.startProlongAnimation(scaleUp ? PROLONG_ANIMATION_AT_START : PROLONG_ANIMATION_AT_END); } } } } @Override public void overridePendingAppTransitionInPlace(String packageName, int anim) { synchronized(mWindowMap) { mAppTransition.overrideInPlaceAppTransition(packageName, anim); } } @Override public void overridePendingAppTransitionMultiThumbFuture( IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, boolean scaleUp) { synchronized(mWindowMap) { mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, callback, scaleUp); } } @Override public void endProlongedAnimations() { synchronized (mWindowMap) { for (final WindowState win : mWindowMap.values()) { final AppWindowToken appToken = win.mAppToken; if (appToken != null && appToken.mAppAnimator != null) { appToken.mAppAnimator.endProlongedAnimation(); } } mAppTransition.notifyProlongedAnimationsEnded(); } } @Override public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Slog.w(TAG_WM, "Execute app transition: " + mAppTransition + " Callers=" + Debug.getCallers(5)); if (mAppTransition.isTransitionSet()) { mAppTransition.setReady(); final long origId = Binder.clearCallingIdentity(); try { mWindowPlacerLocked.performSurfacePlacement(); } finally { Binder.restoreCallingIdentity(origId); } } } } @Override public boolean setAppStartingWindow(IBinder token, String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppStartingWindow()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Slog.v( TAG_WM, "setAppStartingWindow: token=" + token + " pkg=" + pkg + " transferFrom=" + transferFrom); AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null) { Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + token); return false; } // If the display is frozen, we won't do anything until the // actual window is displayed so there is no reason to put in // the starting window. if (!okToDisplay()) { return false; } if (wtoken.startingData != null) { return false; } // If this is a translucent window, then don't // show a starting window -- the current effect (a full-screen // opaque starting window that fades away to the real contents // when it is ready) does not work for this. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x" + Integer.toHexString(theme)); if (theme != 0) { AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, com.android.internal.R.styleable.Window, mCurrentUserId); if (ent == null) { // Whoops! App doesn't exist. Um. Okay. We'll just // pretend like we didn't see that. return false; } final boolean windowIsTranslucent = ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsTranslucent, false); final boolean windowIsFloating = ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsFloating, false); final boolean windowShowWallpaper = ent.array.getBoolean( com.android.internal.R.styleable.Window_windowShowWallpaper, false); final boolean windowDisableStarting = ent.array.getBoolean( com.android.internal.R.styleable.Window_windowDisablePreview, false); if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent + " Floating=" + windowIsFloating + " ShowWallpaper=" + windowShowWallpaper); if (windowIsTranslucent) { return false; } if (windowIsFloating || windowDisableStarting) { return false; } if (windowShowWallpaper) { if (mWallpaperControllerLocked.getWallpaperTarget() == null) { // If this theme is requesting a wallpaper, and the wallpaper // is not currently visible, then this effectively serves as // an opaque window and our starting window transition animation // can still work. We just need to make sure the starting window // is also showing the wallpaper. windowFlags |= FLAG_SHOW_WALLPAPER; } else { return false; } } } if (transferStartingWindow(transferFrom, wtoken)) { return true; } // There is no existing starting window, and the caller doesn't // want us to create one, so that's it! if (!createIfNeeded) { return false; } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData"); wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags); Message m = mH.obtainMessage(H.ADD_STARTING, wtoken); // Note: we really want to do sendMessageAtFrontOfQueue() because we // want to process the message ASAP, before any other queued // messages. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING"); mH.sendMessageAtFrontOfQueue(m); } return true; } private boolean transferStartingWindow(IBinder transferFrom, AppWindowToken wtoken) { if (transferFrom == null) { return false; } AppWindowToken ttoken = findAppWindowToken(transferFrom); if (ttoken == null) { return false; } WindowState startingWindow = ttoken.startingWindow; if (startingWindow != null && ttoken.startingView != null) { // In this case, the starting icon has already been displayed, so start // letting windows get shown immediately without any more transitions. mSkipAppTransitionAnimation = true; if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving existing starting " + startingWindow + " from " + ttoken + " to " + wtoken); final long origId = Binder.clearCallingIdentity(); // Transfer the starting window over to the new token. wtoken.startingData = ttoken.startingData; wtoken.startingView = ttoken.startingView; wtoken.startingDisplayed = ttoken.startingDisplayed; ttoken.startingDisplayed = false; wtoken.startingWindow = startingWindow; wtoken.reportedVisible = ttoken.reportedVisible; ttoken.startingData = null; ttoken.startingView = null; ttoken.startingWindow = null; ttoken.startingMoved = true; startingWindow.mToken = wtoken; startingWindow.mRootToken = wtoken; startingWindow.mAppToken = wtoken; if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) { Slog.v(TAG_WM, "Removing starting window: " + startingWindow); } startingWindow.getWindowList().remove(startingWindow); mWindowsChanged = true; if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing starting " + startingWindow + " from " + ttoken); ttoken.windows.remove(startingWindow); ttoken.allAppWindows.remove(startingWindow); addWindowToListInOrderLocked(startingWindow, true); // Propagate other interesting state between the // tokens. If the old token is displayed, we should // immediately force the new one to be displayed. If // it is animating, we need to move that animation to // the new one. if (ttoken.allDrawn) { wtoken.allDrawn = true; wtoken.deferClearAllDrawn = ttoken.deferClearAllDrawn; } if (ttoken.firstWindowDrawn) { wtoken.firstWindowDrawn = true; } if (!ttoken.hidden) { wtoken.hidden = false; wtoken.hiddenRequested = false; } if (wtoken.clientHidden != ttoken.clientHidden) { wtoken.clientHidden = ttoken.clientHidden; wtoken.sendAppVisibilityToClients(); } ttoken.mAppAnimator.transferCurrentAnimation( wtoken.mAppAnimator, startingWindow.mWinAnimator); updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); getDefaultDisplayContentLocked().layoutNeeded = true; mWindowPlacerLocked.performSurfacePlacement(); Binder.restoreCallingIdentity(origId); return true; } else if (ttoken.startingData != null) { // The previous app was getting ready to show a // starting window, but hasn't yet done so. Steal it! if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving pending starting from " + ttoken + " to " + wtoken); wtoken.startingData = ttoken.startingData; ttoken.startingData = null; ttoken.startingMoved = true; Message m = mH.obtainMessage(H.ADD_STARTING, wtoken); // Note: we really want to do sendMessageAtFrontOfQueue() because we // want to process the message ASAP, before any other queued // messages. mH.sendMessageAtFrontOfQueue(m); return true; } final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator; final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator; if (tAppAnimator.thumbnail != null) { // The old token is animating with a thumbnail, transfer that to the new token. if (wAppAnimator.thumbnail != null) { wAppAnimator.thumbnail.destroy(); } wAppAnimator.thumbnail = tAppAnimator.thumbnail; wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer; wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation; tAppAnimator.thumbnail = null; } return false; } public void removeAppStartingWindow(IBinder token) { synchronized (mWindowMap) { final AppWindowToken wtoken = mTokenMap.get(token).appWindowToken; scheduleRemoveStartingWindowLocked(wtoken); } } public void setAppFullscreen(IBinder token, boolean toOpaque) { synchronized (mWindowMap) { AppWindowToken atoken = findAppWindowToken(token); if (atoken != null) { atoken.appFullscreen = toOpaque; setWindowOpaqueLocked(token, toOpaque); mWindowPlacerLocked.requestTraversal(); } } } public void setWindowOpaque(IBinder token, boolean isOpaque) { synchronized (mWindowMap) { setWindowOpaqueLocked(token, isOpaque); } } public void setWindowOpaqueLocked(IBinder token, boolean isOpaque) { AppWindowToken wtoken = findAppWindowToken(token); if (wtoken != null) { WindowState win = wtoken.findMainWindow(); if (win != null) { win.mWinAnimator.setOpaqueLocked(isOpaque); } } } boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) { boolean delayed = false; if (wtoken.clientHidden == visible) { wtoken.clientHidden = !visible; wtoken.sendAppVisibilityToClients(); } // Allow for state changes and animation to be applied if: // * token is transitioning visibility state // * or the token was marked as hidden and is exiting before we had a chance to play the // transition animation // * or this is an opening app and windows are being replaced. boolean visibilityChanged = false; if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) || (visible && wtoken.waitingForReplacement())) { boolean changed = false; if (DEBUG_APP_TRANSITIONS) Slog.v( TAG_WM, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); boolean runningAppAnimation = false; if (transit != AppTransition.TRANSIT_UNSET) { if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { wtoken.mAppAnimator.setNullAnimation(); } if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) { delayed = runningAppAnimation = true; } WindowState window = wtoken.findMainWindow(); //TODO (multidisplay): Magnification is supported only for the default display. if (window != null && mAccessibilityController != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onAppWindowTransitionLocked(window, transit); } changed = true; } final int windowsCount = wtoken.allAppWindows.size(); for (int i = 0; i < windowsCount; i++) { WindowState win = wtoken.allAppWindows.get(i); if (win == wtoken.startingWindow) { // Starting window that's exiting will be removed when the animation // finishes. Mark all relevant flags for that finishExit will proceed // all the way to actually remove it. if (!visible && win.isVisibleNow() && wtoken.mAppAnimator.isAnimating()) { win.mAnimatingExit = true; win.mRemoveOnExit = true; win.mWindowRemovalAllowed = true; } continue; } //Slog.i(TAG_WM, "Window " + win + ": vis=" + win.isVisible()); //win.dump(" "); if (visible) { if (!win.isVisibleNow()) { if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_ENTER, true); //TODO (multidisplay): Magnification is supported only for the default if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_ENTER); } } changed = true; win.setDisplayLayoutNeeded(); } } else if (win.isVisibleNow()) { if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_EXIT, false); //TODO (multidisplay): Magnification is supported only for the default if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } } changed = true; win.setDisplayLayoutNeeded(); } } wtoken.hidden = wtoken.hiddenRequested = !visible; visibilityChanged = true; if (!visible) { unsetAppFreezingScreenLocked(wtoken, true, true); } else { // If we are being set visible, and the starting window is // not yet displayed, then make sure it doesn't get displayed. WindowState swin = wtoken.startingWindow; if (swin != null && !swin.isDrawnLw()) { swin.mPolicyVisibility = false; swin.mPolicyVisibilityAfterAnim = false; } } if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setTokenVisibilityLocked: " + wtoken + ": hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested); if (changed) { mInputMonitor.setUpdateInputWindowsNeededLw(); if (performLayout) { updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); mWindowPlacerLocked.performSurfacePlacement(); } mInputMonitor.updateInputWindowsLw(false /*force*/); } } if (wtoken.mAppAnimator.animation != null) { delayed = true; } for (int i = wtoken.allAppWindows.size() - 1; i >= 0 && !delayed; i--) { if (wtoken.allAppWindows.get(i).mWinAnimator.isWindowAnimationSet()) { delayed = true; } } if (visibilityChanged) { if (visible && !delayed) { // The token was made immediately visible, there will be no entrance animation. // We need to inform the client the enter animation was finished. wtoken.mEnteringAnimation = true; mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(wtoken.token); } if (!mClosingApps.contains(wtoken) && !mOpeningApps.contains(wtoken)) { // The token is not closing nor opening, so even if there is an animation set, that // doesn't mean that it goes through the normal app transition cycle so we have // to inform the docked controller about visibility change. getDefaultDisplayContentLocked().getDockedDividerController() .notifyAppVisibilityChanged(); } } return delayed; } void updateTokenInPlaceLocked(AppWindowToken wtoken, int transit) { if (transit != AppTransition.TRANSIT_UNSET) { if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { wtoken.mAppAnimator.setNullAnimation(); } applyAnimationLocked(wtoken, null, transit, false, false); } } @Override public void notifyAppStopped(IBinder token, boolean stopped) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "notifyAppStopped()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { final AppWindowToken wtoken; wtoken = findAppWindowToken(token); if (wtoken == null) { Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token); return; } wtoken.notifyAppStopped(stopped); } } @Override public void setAppVisibility(IBinder token, boolean visible) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppVisibility()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; synchronized(mWindowMap) { wtoken = findAppWindowToken(token); if (wtoken == null) { Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token); return; } if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility(" + token + ", visible=" + visible + "): " + mAppTransition + " hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6)); mOpeningApps.remove(wtoken); mClosingApps.remove(wtoken); wtoken.waitingToShow = false; wtoken.hiddenRequested = !visible; if (!visible) { // If the app is dead while it was visible, we kept its dead window on screen. // Now that the app is going invisible, we can remove it. It will be restarted // if made visible again. wtoken.removeAllDeadWindows(); wtoken.setVisibleBeforeClientHidden(); } else if (visible) { if (!mAppTransition.isTransitionSet() && mAppTransition.isReady()) { // Add the app mOpeningApps if transition is unset but ready. This means // we're doing a screen freeze, and the unfreeze will wait for all opening // apps to be ready. mOpeningApps.add(wtoken); } wtoken.startingMoved = false; // If the token is currently hidden (should be the common case), or has been // stopped, then we need to set up to wait for its windows to be ready. if (wtoken.hidden || wtoken.mAppStopped) { wtoken.clearAllDrawn(); // If the app was already visible, don't reset the waitingToShow state. if (wtoken.hidden) { wtoken.waitingToShow = true; } if (wtoken.clientHidden) { // In the case where we are making an app visible // but holding off for a transition, we still need // to tell the client to make its windows visible so // they get drawn. Otherwise, we will wait on // performing the transition until all windows have // been drawn, they never will be, and we are sad. wtoken.clientHidden = false; wtoken.sendAppVisibilityToClients(); } } wtoken.requestUpdateWallpaperIfNeeded(); if (DEBUG_ADD_REMOVE) Slog.v( TAG_WM, "No longer Stopped: " + wtoken); wtoken.mAppStopped = false; } // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. if (okToDisplay() && mAppTransition.isTransitionSet()) { // A dummy animation is a placeholder animation which informs others that an // animation is going on (in this case an application transition). If the animation // was transferred from another application/animator, no dummy animator should be // created since an animation is already in progress. if (wtoken.mAppAnimator.usingTransferredAnimation && wtoken.mAppAnimator.animation == null) { Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken + ", using null transfered animation!"); } if (!wtoken.mAppAnimator.usingTransferredAnimation && (!wtoken.startingDisplayed || mSkipAppTransitionAnimation)) { if (DEBUG_APP_TRANSITIONS) Slog.v( TAG_WM, "Setting dummy animation on: " + wtoken); wtoken.mAppAnimator.setDummyAnimation(); } wtoken.inPendingTransaction = true; if (visible) { mOpeningApps.add(wtoken); wtoken.mEnteringAnimation = true; } else { mClosingApps.add(wtoken); wtoken.mEnteringAnimation = false; } if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) { // We're launchingBehind, add the launching activity to mOpeningApps. final WindowState win = findFocusedWindowLocked(getDefaultDisplayContentLocked()); if (win != null) { final AppWindowToken focusedToken = win.mAppToken; if (focusedToken != null) { if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " + " adding " + focusedToken + " to mOpeningApps"); // Force animation to be loaded. focusedToken.hidden = true; mOpeningApps.add(focusedToken); } } } return; } final long origId = Binder.clearCallingIdentity(); wtoken.inPendingTransaction = false; setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET, true, wtoken.voiceInteraction); wtoken.updateReportedVisibilityLocked(); Binder.restoreCallingIdentity(origId); } } void unsetAppFreezingScreenLocked(AppWindowToken wtoken, boolean unfreezeSurfaceNow, boolean force) { if (wtoken.mAppAnimator.freezingScreen) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + wtoken + " force=" + force); final int N = wtoken.allAppWindows.size(); boolean unfrozeWindows = false; for (int i=0; i