/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import static android.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_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; 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_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; 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_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_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import com.android.internal.app.IBatteryStats; import com.android.internal.app.ThemeUtils; import com.android.internal.os.IDeviceHandler; import com.android.internal.policy.PolicyManager; import com.android.internal.policy.impl.PhoneWindowManager; 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.EventLogTags; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; import com.android.server.display.DisplayManagerService; import com.android.server.input.InputManagerService; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import android.Manifest; import android.app.ActivityManagerNative; import android.app.ActivityOptions; import android.app.IActivityManager; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; import android.animation.ValueAnimator; import android.content.BroadcastReceiver; 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.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.os.Binder; 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.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.FloatMath; import android.util.Log; import android.util.SparseArray; import android.util.Pair; import android.util.Slog; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Choreographer; import android.view.Display; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IApplicationToken; import android.view.IDisplayContentChangeListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindow; import android.view.IWindowManager; import android.view.IWindowSession; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowInfo; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerPolicy.FakeWindow; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.ScaleAnimation; import android.view.animation.Transformation; 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.net.Socket; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs, DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener { static final String TAG = "WindowManager"; static final boolean DEBUG = false; static final boolean DEBUG_ADD_REMOVE = false; static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_ANIM = false; static final boolean DEBUG_LAYOUT = false; static final boolean DEBUG_RESIZE = false; static final boolean DEBUG_LAYERS = false; static final boolean DEBUG_INPUT = false; static final boolean DEBUG_INPUT_METHOD = false; static final boolean DEBUG_VISIBILITY = false; static final boolean DEBUG_WINDOW_MOVEMENT = false; static final boolean DEBUG_TOKEN_MOVEMENT = false; static final boolean DEBUG_ORIENTATION = false; static final boolean DEBUG_APP_ORIENTATION = false; static final boolean DEBUG_CONFIGURATION = false; static final boolean DEBUG_APP_TRANSITIONS = false; static final boolean DEBUG_STARTING_WINDOW = false; static final boolean DEBUG_REORDER = false; static final boolean DEBUG_WALLPAPER = false; static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER; static final boolean DEBUG_DRAG = false; static final boolean DEBUG_SCREEN_ON = false; static final boolean DEBUG_SCREENSHOT = false; static final boolean DEBUG_BOOT = false; static final boolean DEBUG_LAYOUT_REPEATS = true; static final boolean DEBUG_SURFACE_TRACE = false; static final boolean DEBUG_WINDOW_TRACE = false; static final boolean SHOW_SURFACE_ALLOC = false; static final boolean SHOW_TRANSACTIONS = false; static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS; static final boolean HIDE_STACK_CRAWLS = true; 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; /** * Blur surface layer is immediately below dim layer. */ static final int LAYER_OFFSET_BLUR = 2; /** * 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; /** * Layer at which to put the rotation freeze snapshot. */ static final int FREEZE_LAYER = (TYPE_LAYER_MULTIPLIER * 200) + 1; /** * Layer at which to put the mask for emulated screen sizes. */ static final int MASK_LAYER = TYPE_LAYER_MULTIPLIER * 200; /** 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 animate the dim surface from one * value to another, when no window animation is driving it. */ static final int DEFAULT_DIM_DURATION = 200; /** Amount of time (in milliseconds) to animate the fade-in-out transition for * compatible windows. */ static final int DEFAULT_FADE_IN_OUT_DURATION = 400; /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */ static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000; /** Fraction of animation at which the recents thumbnail becomes completely transparent */ static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f; /** * 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; 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"; final private KeyguardDisableHandler mKeyguardDisableHandler; private final boolean mHeadless; private static final float THUMBNAIL_ANIMATION_DECELERATE_FACTOR = 1.5f; private BroadcastReceiver mThemeChangeReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { mUiContext = null; } }; 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); } } }; // Current user when multi-user is enabled. Don't show windows of non-current user. int mCurrentUserId; final Context mContext; private Context mUiContext; final boolean mHaveInputMethods; final boolean mAllowBootMessages; final boolean mLimitedAlphaCompositing; final WindowManagerPolicy mPolicy; final IActivityManager mActivityManager; final IBatteryStats mBatteryStats; /** * All currently active sessions with clients. */ final HashSet mSessions = new HashSet(); /** * Mapping from an IWindow IBinder to the server's Window object. * This is also used as the lock for all of our state. */ final HashMap mWindowMap = new HashMap(); /** * Mapping from a token IBinder to a WindowToken object. */ final HashMap mTokenMap = new HashMap(); /** * Window tokens that are in the process of exiting, but still * on screen for animations. */ final ArrayList mExitingTokens = new ArrayList(); /** * List controlling the ordering of windows in different applications which must * be kept in sync with ActivityManager. */ final ArrayList mAppTokens = new ArrayList(); /** * AppWindowTokens in the Z order they were in at the start of an animation. Between * animations this list is maintained in the exact order of mAppTokens. If tokens * are added to mAppTokens during an animation an attempt is made to insert them at the same * logical location in this list. Note that this list is always in sync with mWindows. */ ArrayList mAnimatingAppTokens = new ArrayList(); /** * Application tokens that are in the process of exiting, but still * on screen for animations. */ final ArrayList mExitingAppTokens = new ArrayList(); /** * 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(); /** * Fake windows added to the window manager. Note: ordered from top to * bottom, opposite of mWindows. */ final ArrayList mFakeWindows = new ArrayList(); /** * Windows that are being resized. Used so we can tell the client about * the resize after closing the transaction in which we resized the * underlying surface. */ final ArrayList 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 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. */ ArrayList mForceRemoves; /** * Windows that clients are waiting to have drawn. */ ArrayList> mWaitingForDrawn = new ArrayList>(); /** * Windows that have called relayout() while we were running animations, * so we need to tell when the animation is done. */ final ArrayList mRelayoutWhileAnimating = new ArrayList(); /** * Used when rebuilding window list to keep track of windows that have * been removed. */ WindowState[] mRebuildTmp = new WindowState[20]; IInputMethodManager mInputMethodManager; final SurfaceSession mFxSession; Watermark mWatermark; StrictModeFlash mStrictModeFlash; final float[] mTmpFloats = new float[9]; boolean mDisplayReady; boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; boolean mForceDisplayEnabled = false; boolean mShowingBootMessages = false; String mLastANRState; /** All DisplayDontents in the world, kept here */ private SparseArray mDisplayContents = new SparseArray(); int mRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; ArrayList mRotationWatchers = new ArrayList(); int mDeferredRotationPauseCount; final Rect mSystemDecorRect = new Rect(); int mSystemDecorLayer = 0; final Rect mScreenRect = new Rect(); boolean mTraversalScheduled = false; boolean mDisplayFrozen = false; boolean mWaitingForConfig = false; boolean mWindowsFreezingScreen = false; boolean mClientFreezingScreen = false; int mAppsFreezingScreen = 0; int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int mLayoutSeq = 0; int mLastStatusBarVisibility = 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. PowerManager.WakeLock mScreenFrozenLock; // State management of app transitions. When we are preparing for a // transition, mNextAppTransition will be the kind of transition to // perform or TRANSIT_NONE if we are not waiting. If we are waiting, // mOpeningApps and mClosingApps are the lists of tokens that will be // made visible or hidden at the next transition. int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; int mNextAppTransitionType = ActivityOptions.ANIM_NONE; String mNextAppTransitionPackage; Bitmap mNextAppTransitionThumbnail; // Used for thumbnail transitions. True if we're scaling up, false if scaling down boolean mNextAppTransitionScaleUp; IRemoteCallback mNextAppTransitionCallback; int mNextAppTransitionEnter; int mNextAppTransitionExit; int mNextAppTransitionStartX; int mNextAppTransitionStartY; int mNextAppTransitionStartWidth; int mNextAppTransitionStartHeight; boolean mAppTransitionReady = false; boolean mAppTransitionRunning = false; boolean mAppTransitionTimeout = false; boolean mStartingIconInTransition = false; boolean mSkipAppTransitionAnimation = false; final ArrayList mOpeningApps = new ArrayList(); final ArrayList mClosingApps = new ArrayList(); 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; int mInputMethodAnimLayerAdjustment; WindowState mInputMethodWindow = null; final ArrayList mInputMethodDialogs = new ArrayList(); boolean mHardKeyboardAvailable; boolean mHardKeyboardEnabled; OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; final ArrayList mWallpaperTokens = new ArrayList(); // If non-null, this is the currently visible window that is associated // with the wallpaper. WindowState mWallpaperTarget = null; // If non-null, we are in the middle of animating from one wallpaper target // to another, and this is the lower one in Z-order. WindowState mLowerWallpaperTarget = null; // If non-null, we are in the middle of animating from one wallpaper target // to another, and this is the higher one in Z-order. private WindowState mUpperWallpaperTarget = null; int mWallpaperAnimLayerAdjustment; float mLastWallpaperX = -1; float mLastWallpaperY = -1; float mLastWallpaperXStep = -1; float mLastWallpaperYStep = -1; // This is set when we are waiting for a wallpaper to tell us it is done // changing its scroll position. WindowState mWaitingOnWallpaper; // The last time we had a timeout when waiting for a wallpaper. long mLastWallpaperTimeoutTime; // We give a wallpaper up to 150ms to finish scrolling. static final long WALLPAPER_TIMEOUT = 150; // Time we wait after a timeout before trying to wait again. static final long WALLPAPER_TIMEOUT_RECOVERY = 10000; AppWindowToken mFocusedApp = null; PowerManagerService mPowerManager; float mWindowAnimationScale = 1.0f; float mTransitionAnimationScale = 1.0f; float mAnimatorDurationScale = 1.0f; final InputManagerService mInputManager; final DisplayManagerService mDisplayManagerService; final DisplayManager mDisplayManager; private boolean mForceDisableHardwareKeyboard = false; // Who is holding the screen on. Session mHoldingScreenOn; PowerManager.WakeLock mHoldingScreenWakeLock; boolean mTurnOnScreen; DragState mDragState = null; /** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple * methods. */ class LayoutFields { static final int SET_UPDATE_ROTATION = 1 << 0; static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1; static final int SET_FORCE_HIDING_CHANGED = 1 << 2; static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3; static final int SET_TURN_ON_SCREEN = 1 << 4; boolean mWallpaperForceHidingChanged = false; boolean mWallpaperMayChange = false; boolean mOrientationChangeComplete = true; int mAdjResult = 0; private Session mHoldScreen = null; private boolean mObscured = false; boolean mDimming = false; private boolean mSyswin = false; private float mScreenBrightness = -1; private float mButtonBrightness = -1; private long mUserActivityTimeout = -1; private boolean mUpdateRotation = false; private static final int DISPLAY_CONTENT_UNKNOWN = 0; private static final int DISPLAY_CONTENT_MIRROR = 1; private static final int DISPLAY_CONTENT_UNIQUE = 2; private int mDisplayHasContent = DISPLAY_CONTENT_UNKNOWN; } final LayoutFields mInnerFields = new LayoutFields(); static class AppWindowAnimParams { AppWindowAnimator mAppAnimator; ArrayList mWinAnimators; public AppWindowAnimParams(final AppWindowAnimator appAnimator) { mAppAnimator = appAnimator; final AppWindowToken atoken = appAnimator.mAppToken; mWinAnimators = new ArrayList(); final int N = atoken.allAppWindows.size(); for (int i = 0; i < N; i++) { mWinAnimators.add(atoken.allAppWindows.get(i).mWinAnimator); } } } static class LayoutToAnimatorParams { boolean mParamsModified; static final long WALLPAPER_TOKENS_CHANGED = 1 << 0; long mChanges; boolean mAnimationScheduled; SparseArray mWinAnimatorLists = new SparseArray(); WindowState mWallpaperTarget; WindowState mLowerWallpaperTarget; WindowState mUpperWallpaperTarget; SparseArray mDimParams = new SparseArray(); ArrayList mWallpaperTokens = new ArrayList(); ArrayList mAppWindowAnimParams = new ArrayList(); } /** Params from WindowManagerService to WindowAnimator. Do not modify or read without first * locking on either mWindowMap or mAnimator and then on mLayoutToAnim */ final LayoutToAnimatorParams mLayoutToAnim = new LayoutToAnimatorParams(); /** The lowest wallpaper target with a detached wallpaper animation on it. */ WindowState mWindowDetachedWallpaper = null; /** 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. */ private int mTransactionSequence; /** Only do a maximum of 6 repeated layouts. After that quit */ private int mLayoutRepeatCount; final WindowAnimator mAnimator; final class DragInputEventReceiver extends InputEventReceiver { 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(); switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: { if (DEBUG_DRAG) { Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer"); } } break; case MotionEvent.ACTION_MOVE: { 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, "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, "Drag cancelled!"); endDrag = true; } break; } if (endDrag) { if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state"); // tell all the windows that the drag has ended synchronized (mWindowMap) { mDragState.endDragLw(); } } handled = true; } } catch (Exception e) { Slog.e(TAG, "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 = true; // Temp regions for intermediary calculations. private final Region mTempRegion = new Region(); private ViewServer mViewServer; private ArrayList mWindowChangeListeners = new ArrayList(); private 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; public static WindowManagerService main(final Context context, final PowerManagerService pm, final DisplayManagerService dm, final InputManagerService im, final IDeviceHandler device, final Handler uiHandler, final Handler wmHandler, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; wmHandler.runWithScissors(new Runnable() { @Override public void run() { holder[0] = new WindowManagerService(context, pm, dm, im, device, uiHandler, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0]; } private void initPolicy(Handler uiHandler) { uiHandler.runWithScissors(new Runnable() { @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; } }, 0); } private WindowManagerService(Context context, PowerManagerService pm, DisplayManagerService displayManager, InputManagerService inputManager, IDeviceHandler device, Handler uiHandler, 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); mDisplayManagerService = displayManager; mHeadless = displayManager.isHeadless(); mPolicy = PolicyManager.makeNewWindowManager(device); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mDisplayManager.registerDisplayListener(this, null); Display[] displays = mDisplayManager.getDisplays(); for (Display display : displays) { createDisplayContentLocked(display); } mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); mPowerManager = pm; mPowerManager.setPolicy(mPolicy); PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mScreenFrozenLock = pmc.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); mScreenFrozenLock.setReferenceCounted(false); mActivityManager = ActivityManagerNative.getDefault(); mBatteryStats = BatteryStatsService.getService(); // Get persisted window scale setting mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); mTransitionAnimationScale = Settings.Global.getFloat(context.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale)); // 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); mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG); mHoldingScreenWakeLock.setReferenceCounted(false); mInputManager = inputManager; mFxSession = new SurfaceSession(); mAnimator = new WindowAnimator(this); mForceDisableHardwareKeyboard = context.getResources().getBoolean( com.android.internal.R.bool.config_forceDisableHardwareKeyboard); initPolicy(uiHandler); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); Surface.openTransaction(); try { createWatermarkInTransaction(); } finally { Surface.closeTransaction(); } ThemeUtils.registerThemeChangeReceiver(mContext, mThemeChangeReceiver); } private Context getUiContext() { if (mUiContext == null) { mUiContext = ThemeUtils.createUiContext(mContext); } return mUiContext != null ? mUiContext : mContext; } 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)) { Log.wtf(TAG, "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, "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(); final int i = windows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " + i + " of " + windows.size() + " (before " + pos + ")"); 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. */ 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.mDisplayContent == 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 void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { final IWindow client = win.mClient; final WindowToken token = win.mToken; final DisplayContent displayContent = win.mDisplayContent; final WindowList windows = win.getWindowList(); final int N = windows.size(); final WindowState attached = win.mAttachedWindow; int i; WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); if (attached == null) { int tokenWindowsPos = 0; int windowListPos = tokenWindowList.size(); if (token.appWindowToken != null) { int index = windowListPos - 1; if (index >= 0) { // 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; WindowState lastWindow = tokenWindowList.get(index); 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, "Adding window " + win + " at " + (newIdx + 1) + " of " + N); } 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; } } } else { // No windows from this token on this display if (localLOGV) Slog.v( TAG, "Figuring out where to add app window " + client.asBinder() + " (token=" + token + ")"); // Figure out where the window should go, based on the // order of applications. final int NA = mAnimatingAppTokens.size(); WindowState pos = null; for (i=NA-1; i>=0; i--) { AppWindowToken t = mAnimatingAppTokens.get(i); if (t == token) { i--; 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, win.mDisplayContent); if (!t.sendingToBottom && tokenWindowList.size() > 0) { pos = tokenWindowList.get(0); } } // 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, win.mDisplayContent); final int NC = tokenWindowList.size(); if (NC > 0) { WindowState bottom = tokenWindowList.get(0); if (bottom.mSubLayer < 0) { pos = bottom; } } } placeWindowBefore(pos, win); } else { // Continue looking down until we find the first // token that has windows on this display. while (i >= 0) { AppWindowToken t = mAnimatingAppTokens.get(i); tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent); final int NW = tokenWindowList.size(); if (NW > 0) { pos = tokenWindowList.get(NW-1); break; } i--; } 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); } else { // Just search for the start of this layer. final int myLayer = win.mBaseLayer; for (i=0; i myLayer) { break; } } if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { Slog.v(TAG, "Adding window " + win + " at " + i + " of " + N); } windows.add(i, win); mWindowsChanged = true; } } } } else { // Figure out where window should go, based on layer. final int myLayer = win.mBaseLayer; for (i=N-1; i>=0; i--) { if (windows.get(i).mBaseLayer <= myLayer) { break; } } i++; if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + win + " at " + i + " of " + N); windows.add(i, win); mWindowsChanged = true; } if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); token.windows.add(tokenWindowsPos, win); } } else { // 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; for (i=0; i= 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, "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, "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, "Adding " + win + " to " + token); token.windows.add(win); } if (sublayer < 0) { placeWindowBefore(attached, win); } else { placeWindowAfter(largestSublayer >= 0 ? windowWithLargestSublayer : attached, win); } } } if (win.mAppToken != null && addToToken) { win.mAppToken.allAppWindows.add(win); } } /** TODO(cmautner): Is this the same as {@link WindowState#canReceiveKeys()} */ static boolean canBeImeTarget(WindowState w) { final int fl = w.mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) || w.mAttrs.type == TYPE_APPLICATION_STARTING) { if (DEBUG_INPUT_METHOD) { Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding()); if (!w.isVisibleOrAdding()) { Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurface + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility + " policyVis=" + w.mPolicyVisibility + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim + " attachHid=" + w.mAttachedHidden + " exiting=" + w.mExiting + " destroying=" + w.mDestroying); if (w.mAppToken != null) { Slog.i(TAG, " 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(); final int N = windows.size(); WindowState w = null; int i = N; while (i > 0) { i--; w = windows.get(i); if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG, "Checking window @" + i + " " + w + " fl=0x" + Integer.toHexString(w.mAttrs.flags)); if (canBeImeTarget(w)) { //Slog.i(TAG, "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, "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 && w != null && curTarget.isDisplayedLw() && curTarget.isClosing() && (curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) { if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, not changing"); return windows.indexOf(curTarget) + 1; } if (DEBUG_INPUT_METHOD) Slog.v(TAG, "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, "mNextAppTransition=" + mNextAppTransition + " " + highestTarget + " animating=" + highestTarget.mWinAnimator.isAnimating() + " layer=" + highestTarget.mWinAnimator.mAnimLayer + " new layer=" + w.mWinAnimator.mAnimLayer); if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // 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.isAnimating() && 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, "Placing input method @" + (i+1)); if (w != null) { if (willMove) { if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to " + w + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4))); mInputMethodTarget = w; mInputMethodTargetWaitingAnim = false; if (w.mAppToken != null) { setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment); } else { setInputMethodAnimLayerAdjustment(0); } } return i+1; } if (willMove) { if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to null." + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4))); mInputMethodTarget = null; 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, "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); } void setInputMethodAnimLayerAdjustment(int adj) { if (DEBUG_LAYERS) Slog.v(TAG, "Setting im layer adj to " + adj); mInputMethodAnimLayerAdjustment = adj; WindowState imw = mInputMethodWindow; if (imw != null) { imw.mWinAnimator.mAnimLayer = imw.mLayer + adj; if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + " anim layer: " + imw.mWinAnimator.mAnimLayer); int wi = imw.mChildWindows.size(); while (wi > 0) { wi--; WindowState cw = imw.mChildWindows.get(wi); cw.mWinAnimator.mAnimLayer = cw.mLayer + adj; if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw + " anim layer: " + cw.mWinAnimator.mAnimLayer); } } int di = mInputMethodDialogs.size(); while (di > 0) { di --; imw = mInputMethodDialogs.get(di); imw.mWinAnimator.mAnimLayer = imw.mLayer + adj; if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + " anim layer: " + imw.mWinAnimator.mAnimLayer); } } 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, "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, "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, "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, 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, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; if (pos < windows.size()) { WindowState wp = windows.get(pos); if (wp == mInputMethodWindow) { pos++; } } if (DEBUG_INPUT_METHOD) Slog.v(TAG, "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) { // All is good! return false; } } if (imWin != null) { if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "Moving IM from " + imPos); logWindowList(windows, " "); } imPos = tmpRemoveWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List after removing with new pos " + imPos + ":"); logWindowList(windows, " "); } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "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, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); imWin.mTargetAppToken = null; reAddWindowToListInOrderLocked(imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List with no IM target:"); logWindowList(windows, " "); } if (DN > 0) moveInputMethodDialogsLocked(-1); } else { moveInputMethodDialogsLocked(-1); } } if (needAssignLayers) { assignLayersLocked(windows); } return true; } void adjustInputMethodDialogsLocked() { moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); } final boolean isWallpaperVisible(WindowState wallpaperTarget) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured=" + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null) ? wallpaperTarget.mAppToken.mAppAnimator.animation : null) + " upper=" + mUpperWallpaperTarget + " lower=" + mLowerWallpaperTarget); return (wallpaperTarget != null && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null && wallpaperTarget.mAppToken.mAppAnimator.animation != null))) || mUpperWallpaperTarget != null || mLowerWallpaperTarget != null; } static final int ADJUST_WALLPAPER_LAYERS_CHANGED = 1<<1; static final int ADJUST_WALLPAPER_VISIBILITY_CHANGED = 1<<2; int adjustWallpaperWindowsLocked() { mInnerFields.mWallpaperMayChange = false; int changed = 0; boolean targetChanged = false; // TODO(multidisplay): Wallpapers on main screen only. final DisplayInfo displayInfo = getDefaultDisplayContentLocked().getDisplayInfo(); final int dw = displayInfo.appWidth; final int dh = displayInfo.appHeight; // First find top-most window that has asked to be on top of the // wallpaper; all wallpapers go behind it. final WindowList windows = getDefaultWindowListLocked(); int N = windows.size(); WindowState w = null; WindowState foundW = null; int foundI = 0; WindowState topCurW = null; int topCurI = 0; int windowDetachedI = -1; int i = N; while (i > 0) { i--; w = windows.get(i); if ((w.mAttrs.type == TYPE_WALLPAPER)) { if (topCurW == null) { topCurW = w; topCurI = i; } continue; } topCurW = null; if (w != mWindowDetachedWallpaper && w.mAppToken != null) { // If this window's app token is hidden and not animating, // it is of no interest to us. if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w); continue; } } if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": readyfordisplay=" + w.isReadyForDisplay() + " mDrawState=" + w.mWinAnimator.mDrawState); if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay() && (mWallpaperTarget == w || w.isDrawnLw())) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w); foundW = w; foundI = i; if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) { // The current wallpaper target is animating, so we'll // look behind it for another possible target and figure // out what is going on below. if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": token animating, looking behind."); continue; } break; } else if (w == mWindowDetachedWallpaper) { windowDetachedI = i; } } if (foundW == null && windowDetachedI >= 0) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Found animating detached wallpaper activity: #" + i + "=" + w); foundW = w; foundI = windowDetachedI; } if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // If we are currently waiting for an app transition, and either // the current target or the next target are involved with it, // then hold off on doing anything with the wallpaper. // Note that we are checking here for just whether the target // is part of an app token... which is potentially overly aggressive // (the app token may not be involved in the transition), but good // enough (we'll just wait until whatever transition is pending // executes). if (mWallpaperTarget != null && mWallpaperTarget.mAppToken != null) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper not changing: waiting for app anim in current target"); return 0; } if (foundW != null && foundW.mAppToken != null) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper not changing: waiting for app anim in found target"); return 0; } } if (mWallpaperTarget != foundW && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) { if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "New wallpaper target: " + foundW + " oldTarget: " + mWallpaperTarget); } mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; WindowState oldW = mWallpaperTarget; mWallpaperTarget = foundW; targetChanged = true; // Now what is happening... if the current and new targets are // animating, then we are in our super special mode! if (foundW != null && oldW != null) { boolean oldAnim = oldW.mWinAnimator.mAnimation != null || (oldW.mAppToken != null && oldW.mAppToken.mAppAnimator.animation != null); boolean foundAnim = foundW.mWinAnimator.mAnimation != null || (foundW.mAppToken != null && foundW.mAppToken.mAppAnimator.animation != null); if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "New animation: " + foundAnim + " old animation: " + oldAnim); } if (foundAnim && oldAnim) { int oldI = windows.indexOf(oldW); if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); } if (oldI >= 0) { if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#" + foundI + "=" + foundW); } // Set the new target correctly. if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) { if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "Old wallpaper still the target."); } mWallpaperTarget = oldW; foundW = oldW; foundI = oldI; } // Now set the upper and lower wallpaper targets // correctly, and make sure that we are positioning // the wallpaper below the lower. else if (foundI > oldI) { // The new target is on top of the old one. if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "Found target above old target."); } mUpperWallpaperTarget = foundW; mLowerWallpaperTarget = oldW; foundW = oldW; foundI = oldI; } else { // The new target is below the old one. if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "Found target below old target."); } mUpperWallpaperTarget = oldW; mLowerWallpaperTarget = foundW; } } } } } else if (mLowerWallpaperTarget != null) { // Is it time to stop animating? boolean lowerAnimating = mLowerWallpaperTarget.mWinAnimator.mAnimation != null || (mLowerWallpaperTarget.mAppToken != null && mLowerWallpaperTarget.mAppToken.mAppAnimator.animation != null); boolean upperAnimating = mUpperWallpaperTarget.mWinAnimator.mAnimation != null || (mUpperWallpaperTarget.mAppToken != null && mUpperWallpaperTarget.mAppToken.mAppAnimator.animation != null); if (!lowerAnimating || !upperAnimating) { if (DEBUG_WALLPAPER_LIGHT) { Slog.v(TAG, "No longer animating wallpaper targets!"); } mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; mWallpaperTarget = foundW; targetChanged = true; } } boolean visible = foundW != null; if (visible) { // The window is visible to the compositor... but is it visible // to the user? That is what the wallpaper cares about. visible = isWallpaperVisible(foundW); if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible); // If the wallpaper target is animating, we may need to copy // its layer adjustment. Only do this if we are not transfering // between two wallpaper targets. mWallpaperAnimLayerAdjustment = (mLowerWallpaperTarget == null && foundW.mAppToken != null) ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0; final int maxLayer = mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; // Now w is the window we are supposed to be behind... but we // need to be sure to also be behind any of its attached windows, // AND any starting window associated with it, AND below the // maximum layer the policy allows for wallpapers. while (foundI > 0) { WindowState wb = windows.get(foundI-1); if (wb.mBaseLayer < maxLayer && wb.mAttachedWindow != foundW && (foundW.mAttachedWindow == null || wb.mAttachedWindow != foundW.mAttachedWindow) && (wb.mAttrs.type != TYPE_APPLICATION_STARTING || foundW.mToken == null || wb.mToken != foundW.mToken)) { // This window is not related to the previous one in any // interesting way, so stop here. break; } foundW = wb; foundI--; } } else { if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target"); } if (foundW == null && topCurW != null) { // There is no wallpaper target, so it goes at the bottom. // We will assume it is the same place as last time, if known. foundW = topCurW; foundI = topCurI+1; } else { // Okay i is the position immediately above the wallpaper. Look at // what is below it for later. foundW = foundI > 0 ? windows.get(foundI-1) : null; } if (visible) { if (mWallpaperTarget.mWallpaperX >= 0) { mLastWallpaperX = mWallpaperTarget.mWallpaperX; mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; } if (mWallpaperTarget.mWallpaperY >= 0) { mLastWallpaperY = mWallpaperTarget.mWallpaperY; mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; } } // Start stepping backwards from here, ensuring that our wallpaper windows // are correctly placed. int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); if (token.hidden == visible) { if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "Wallpaper token " + token + " hidden=" + !visible); changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED; token.hidden = !visible; // Need to do a layout to ensure the wallpaper now has the // correct size. getDefaultDisplayContentLocked().layoutNeeded = true; } int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); if (visible) { updateWallpaperOffsetLocked(wallpaper, dw, dh, false); } // First, make sure the client has the current visibility // state. dispatchWallpaperVisibility(wallpaper, visible); wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment; if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win " + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); // First, if this window is at the current index, then all // is well. if (wallpaper == foundW) { foundI--; foundW = foundI > 0 ? windows.get(foundI-1) : null; continue; } // The window didn't match... the current wallpaper window, // wherever it is, is in the wrong place, so make sure it is // not in the list. int oldIndex = windows.indexOf(wallpaper); if (oldIndex >= 0) { if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " + oldIndex + ": " + wallpaper); windows.remove(oldIndex); mWindowsChanged = true; if (oldIndex < foundI) { foundI--; } } // Now stick it in. if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { Slog.v(TAG, "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + foundI); } windows.add(foundI, wallpaper); mWindowsChanged = true; changed |= ADJUST_WALLPAPER_LAYERS_CHANGED; } } if (targetChanged && DEBUG_WALLPAPER_LIGHT) { Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper=" + mUpperWallpaperTarget); } return changed; } void setWallpaperAnimLayerAdjustmentLocked(int adj) { if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Setting wallpaper layer adj to " + adj); mWallpaperAnimLayerAdjustment = adj; int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + adj; if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "setWallpaper win " + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); } } } boolean updateWallpaperOffsetLocked(WindowState wallpaperWin, int dw, int dh, boolean sync) { boolean changed = false; boolean rawChanged = false; float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f; float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw; int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0; changed = wallpaperWin.mXOffset != offset; if (changed) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset); wallpaperWin.mXOffset = offset; } if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) { wallpaperWin.mWallpaperX = wpx; wallpaperWin.mWallpaperXStep = wpxs; rawChanged = true; } float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh; offset = availh > 0 ? -(int)(availh*wpy+.5f) : 0; if (wallpaperWin.mYOffset != offset) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset); changed = true; wallpaperWin.mYOffset = offset; } if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) { wallpaperWin.mWallpaperY = wpy; wallpaperWin.mWallpaperYStep = wpys; rawChanged = true; } if (rawChanged && (wallpaperWin.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { try { if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " + wallpaperWin + " x=" + wallpaperWin.mWallpaperX + " y=" + wallpaperWin.mWallpaperY); if (sync) { mWaitingOnWallpaper = wallpaperWin; } wallpaperWin.mClient.dispatchWallpaperOffsets( wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY, wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync); if (sync) { if (mWaitingOnWallpaper != null) { long start = SystemClock.uptimeMillis(); if ((mLastWallpaperTimeoutTime+WALLPAPER_TIMEOUT_RECOVERY) < start) { try { if (DEBUG_WALLPAPER) Slog.v(TAG, "Waiting for offset complete..."); mWindowMap.wait(WALLPAPER_TIMEOUT); } catch (InterruptedException e) { } if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!"); if ((start+WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) { Slog.i(TAG, "Timeout waiting for wallpaper to offset: " + wallpaperWin); mLastWallpaperTimeoutTime = start; } } mWaitingOnWallpaper = null; } } } catch (RemoteException e) { } } return changed; } void wallpaperOffsetsComplete(IBinder window) { synchronized (mWindowMap) { if (mWaitingOnWallpaper != null && mWaitingOnWallpaper.mClient.asBinder() == window) { mWaitingOnWallpaper = null; mWindowMap.notifyAll(); } } } void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { final DisplayContent displayContent = changingTarget.mDisplayContent; final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final int dw = displayInfo.appWidth; final int dh = displayInfo.appHeight; WindowState target = mWallpaperTarget; if (target != null) { if (target.mWallpaperX >= 0) { mLastWallpaperX = target.mWallpaperX; } else if (changingTarget.mWallpaperX >= 0) { mLastWallpaperX = changingTarget.mWallpaperX; } if (target.mWallpaperY >= 0) { mLastWallpaperY = target.mWallpaperY; } else if (changingTarget.mWallpaperY >= 0) { mLastWallpaperY = changingTarget.mWallpaperY; } } int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); if (updateWallpaperOffsetLocked(wallpaper, dw, dh, sync)) { WindowStateAnimator winAnimator = wallpaper.mWinAnimator; winAnimator.computeShownFrameLocked(); // No need to lay out the windows - we can just set the wallpaper position // directly. // TODO(cmautner): Don't move this from here, just lock the WindowAnimator. if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left || winAnimator.mSurfaceY != wallpaper.mShownFrame.top) { winAnimator.setWallpaperOffset((int) wallpaper.mShownFrame.left, (int) wallpaper.mShownFrame.top); } // We only want to be synchronous with one wallpaper. sync = false; } } } } /** * Check wallpaper for visiblity change and notify window if so. * @param wallpaper The wallpaper to test and notify. * @param visible Current visibility. */ void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) { if (wallpaper.mWallpaperVisible != visible) { wallpaper.mWallpaperVisible = visible; try { if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Updating vis of wallpaper " + wallpaper + ": " + visible + " from:\n" + Debug.getCallers(4, " ")); wallpaper.mClient.dispatchAppVisibility(visible); } catch (RemoteException e) { } } } void updateWallpaperVisibilityLocked() { final boolean visible = isWallpaperVisible(mWallpaperTarget); final DisplayContent displayContent = mWallpaperTarget.mDisplayContent; final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final int dw = displayInfo.appWidth; final int dh = displayInfo.appHeight; int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); if (token.hidden == visible) { token.hidden = !visible; // Need to do a layout to ensure the wallpaper now has the // correct size. getDefaultDisplayContentLocked().layoutNeeded = true; } int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); if (visible) { updateWallpaperOffsetLocked(wallpaper, dw, dh, false); } dispatchWallpaperVisibility(wallpaper, visible); } } } public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { int res = mPolicy.checkAddPermission(attrs); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } boolean reportNewConfig = false; WindowState attachedWindow = null; WindowState win = 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) { return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "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, "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, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG, "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, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream 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) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { Slog.w(TAG, "Attempted to add window with non-application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { Slog.w(TAG, "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 (localLOGV) Slog.v( TAG, "**** 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, "Attempted to add input method 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, "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, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } win = new WindowState(this, session, client, token, attachedWindow, seq, attrs, viewVisibility, displayContent); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. Slog.w(TAG, "Adding window client " + client.asBinder() + " that is dead, aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } mPolicy.adjustWindowParamsLw(win.mAttrs); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); } // From now on, no exceptions or errors allowed! res = WindowManagerGlobal.ADD_OKAY; origId = Binder.clearCallingIdentity(); if (addToken) { mTokenMap.put(attrs.token, token); } win.attach(); mWindowMap.put(client.asBinder(), win); if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "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); adjustInputMethodDialogsLocked(); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (type == TYPE_WALLPAPER) { mLastWallpaperTimeoutTime = 0; adjustWallpaperWindowsLocked(); } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } else if (mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer) { // 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. adjustWallpaperWindowsLocked(); } } win.mWinAnimator.mEnterAnimationPending = true; if (displayContent.isDefaultDisplay) { mPolicy.getContentInsetHintLw(attrs, outContentInsets); } else { outContentInsets.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); } assignLayersLocked(displayContent.getWindowList()); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. //dump(); if (focusChanged) { finishUpdateFocusedWindowAfterAssignLayersLocked(false /*updateInputWindows*/); } mInputMonitor.updateInputWindowsLw(false /*force*/); if (localLOGV) Slog.v( TAG, "New client " + client.asBinder() + ": window=" + win); if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; } } if (reportNewConfig) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return res; } public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } removeWindowLocked(session, win); } } public void removeWindowLocked(Session session, WindowState win) { if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Remove " + win + " client=" + Integer.toHexString(System.identityHashCode( win.mClient.asBinder())) + ", surface=" + win.mWinAnimator.mSurface); final long origId = Binder.clearCallingIdentity(); win.disposeInputChannel(); if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurface + " mExiting=" + win.mExiting + " isAnimating=" + win.mWinAnimator.isAnimating() + " app-animation=" + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null) + " inPendingTransaction=" + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false) + " mDisplayFrozen=" + mDisplayFrozen); // 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()) { // If we are not currently running the exit animation, we // need to see about starting one. wasVisible = win.isWinVisibleLw(); if (wasVisible) { int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } // Try starting an animation. if (win.mWinAnimator.applyAnimationLocked(transit, false)) { win.mExiting = true; } scheduleNotifyWindowTranstionIfNeededLocked(win, transit); } if (win.mExiting || win.mWinAnimator.isAnimating()) { // The exit animation is running... wait for it! //Slog.i(TAG, "*** Running exit animation..."); win.mExiting = true; win.mRemoveOnExit = true; win.mDisplayContent.layoutNeeded = true; updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); performLayoutAndPlaceSurfacesLocked(); mInputMonitor.updateInputWindowsLw(false /*force*/); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } //dump(); Binder.restoreCallingIdentity(origId); return; } } removeWindowInnerLocked(session, win); // Removing a visible window will effect the computed orientation // So just update orientation if needed. if (wasVisible && computeForcedAppOrientationLocked() != mForcedAppOrientation && updateOrientationFromAppTokensLocked(false)) { mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); Binder.restoreCallingIdentity(origId); } private void removeWindowInnerLocked(Session session, WindowState win) { if (win.mRemoved) { // Nothing to do. return; } for (int i=win.mChildWindows.size()-1; i>=0; i--) { WindowState cwin = win.mChildWindows.get(i); Slog.w(TAG, "Force-removing child win " + cwin + " from container " + win); removeWindowInnerLocked(cwin.mSession, cwin); } win.mRemoved = true; if (mInputMethodTarget == win) { moveInputMethodWindowsIfNeededLocked(false); } if (false) { RuntimeException e = new RuntimeException("here"); e.fillInStackTrace(); Slog.w(TAG, "Removing window " + win, e); } mPolicy.removeWindowLw(win); win.removeLocked(); if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win); mWindowMap.remove(win.mClient.asBinder()); final WindowList windows = win.getWindowList(); windows.remove(win); mPendingRemove.remove(win); mResizingWindows.remove(win); mWindowsChanged = true; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "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, "Removing " + win + " from " + token); token.windows.remove(win); if (atoken != null) { atoken.allAppWindows.remove(win); } if (localLOGV) Slog.v( TAG, "**** 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; } } if (atoken != null) { if (atoken.startingWindow == win) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling startingWindow " + win); atoken.startingWindow = null; } 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, "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. if (DEBUG_STARTING_WINDOW) { Slog.v(TAG, "Schedule remove starting " + token + ": no more real windows"); } Message m = mH.obtainMessage(H.REMOVE_STARTING, atoken); mH.sendMessage(m); } } if (win.mAttrs.type == TYPE_WALLPAPER) { mLastWallpaperTimeoutTime = 0; adjustWallpaperWindowsLocked(); } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } if (!mInLayout) { assignLayersLocked(windows); win.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } } mInputMonitor.updateInputWindowsLw(true /*force*/); } static void logSurface(WindowState w, String msg, RuntimeException where) { String str = " SURFACE " + msg + ": " + w; if (where != null) { Slog.i(TAG, str, where); } else { Slog.i(TAG, str); } } static void logSurface(Surface s, String title, String msg, RuntimeException where) { String str = " SURFACE " + s + ": " + msg + " / " + title; if (where != null) { Slog.i(TAG, str, where); } else { Slog.i(TAG, str); } } // TODO(cmautner): Move to WindowStateAnimator. void setTransparentRegionHint(final WindowStateAnimator winAnimator, final Region region) { mH.sendMessage(mH.obtainMessage(H.SET_TRANSPARENT_REGION, new Pair(winAnimator, region))); } void setTransparentRegionWindow(Session session, IWindow client, Region region) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { WindowState w = windowForClientLocked(session, client, false); if ((w != null) && w.mHasSurface) { setTransparentRegionHint(w.mWinAnimator, 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 (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.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } } 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 setWindowWallpaperPositionLocked(WindowState window, float x, float y, float xStep, float yStep) { if (window.mWallpaperX != x || window.mWallpaperY != y) { window.mWallpaperX = x; window.mWallpaperY = y; window.mWallpaperXStep = xStep; window.mWallpaperYStep = yStep; updateWallpaperOffsetLocked(window, true); } } void wallpaperCommandComplete(IBinder window, Bundle result) { synchronized (mWindowMap) { if (mWaitingOnWallpaper != null && mWaitingOnWallpaper.mClient.asBinder() == window) { mWaitingOnWallpaper = null; mWindowMap.notifyAll(); } } } public Bundle sendWindowWallpaperCommandLocked(WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) { if (window == mWallpaperTarget || window == mLowerWallpaperTarget || window == mUpperWallpaperTarget) { boolean doWait = sync; int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); try { wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync); // We only want to be synchronous with one wallpaper. sync = false; } catch (RemoteException e) { } } } if (doWait) { // XXX Need to wait for result. } } return null; } public void setUniverseTransformLocked(WindowState window, float alpha, float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy) { Transformation transform = window.mWinAnimator.mUniverseTransform; transform.setAlpha(alpha); Matrix matrix = transform.getMatrix(); matrix.getValues(mTmpFloats); mTmpFloats[Matrix.MTRANS_X] = offx; mTmpFloats[Matrix.MTRANS_Y] = offy; mTmpFloats[Matrix.MSCALE_X] = dsdx; mTmpFloats[Matrix.MSKEW_Y] = dtdx; mTmpFloats[Matrix.MSKEW_X] = dsdy; mTmpFloats[Matrix.MSCALE_Y] = dtdy; matrix.setValues(mTmpFloats); final DisplayInfo displayInfo = window.mDisplayContent.getDisplayInfo(); final RectF dispRect = new RectF(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); matrix.mapRect(dispRect); window.mGivenTouchableRegion.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top, (int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE); window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; window.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { synchronized (mWindowMap) { WindowState window = mWindowMap.get(token); if (window != null) { scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(window, rectangle, immediate); } } } private void scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(WindowState window, Rect rectangle, boolean immediate) { DisplayContent displayContent = window.mDisplayContent; if (displayContent.mDisplayContentChangeListeners != null && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) { mH.obtainMessage(H.NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, displayContent.getDisplayId(), immediate? 1 : 0, new Rect(rectangle)).sendToTarget(); } } private void handleNotifyRectangleOnScreenRequested(int displayId, Rect rectangle, boolean immediate) { RemoteCallbackList callbacks = null; synchronized (mWindowMap) { DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent == null) { return; } callbacks = displayContent.mDisplayContentChangeListeners; if (callbacks == null) { return; } } final int callbackCount = callbacks.beginBroadcast(); try { for (int i = 0; i < callbackCount; i++) { try { callbacks.getBroadcastItem(i).onRectangleOnScreenRequested(displayId, rectangle, immediate); } catch (RemoteException re) { /* ignore */ } } } finally { callbacks.finishBroadcast(); } } public int relayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { boolean toBeDisplayed = false; boolean inTouchMode; boolean configChanged; boolean surfaceChanged = false; boolean animating; // if they don't have this permission, mask out the status bar bits int systemUiVisibility = 0; if (attrs != null) { systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility); if ((systemUiVisibility & StatusBarManager.DISABLE_MASK) != 0) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { systemUiVisibility &= ~StatusBarManager.DISABLE_MASK; } } } long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator. WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } WindowStateAnimator winAnimator = win.mWinAnimator; if (win.mRequestedWidth != requestedWidth || win.mRequestedHeight != requestedHeight) { win.mLayoutNeeded = true; win.mRequestedWidth = requestedWidth; win.mRequestedHeight = requestedHeight; } if (attrs != null && seq == win.mSeq) { win.mSystemUiVisibility = systemUiVisibility; } if (attrs != null) { mPolicy.adjustWindowParamsLw(attrs); } winAnimator.mSurfaceDestroyDeferred = (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0; int attrChanges = 0; int flagChanges = 0; if (attrs != null) { if (win.mAttrs.type != attrs.type) { throw new IllegalArgumentException( "Window type can not be changed after the window is added."); } 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, "Relayout " + win + ": viewVisibility=" + viewVisibility + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { winAnimator.mAlpha = attrs.alpha; } final boolean scaledWindow = ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0); if (scaledWindow) { // requested{Width|Height} Surface's physical size // attrs.{width|height} Size on screen win.mHScale = (attrs.width != requestedWidth) ? (attrs.width / (float)requestedWidth) : 1.0f; win.mVScale = (attrs.height != requestedHeight) ? (attrs.height / (float)requestedHeight) : 1.0f; } else { win.mHScale = win.mVScale = 1; } 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; win.mRelayoutCalled = true; final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; if (DEBUG_SCREEN_ON) { RuntimeException stack = new RuntimeException(); stack.fillInStackTrace(); Slog.i(TAG, "Relayout " + win + ": oldVis=" + oldVisibility + " newVis=" + viewVisibility, stack); } if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { toBeDisplayed = !win.isVisibleLw(); if (win.mExiting) { winAnimator.cancelExitAnimationForNextAnimationLocked(); win.mExiting = false; } if (win.mDestroying) { win.mDestroying = false; mDestroySurface.remove(win); } if (oldVisibility == View.GONE) { winAnimator.mEnterAnimationPending = true; } if (toBeDisplayed) { if (win.isDrawnLw() && okToDisplay()) { winAnimator.applyEnterAnimationLocked(); } if ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Relayout window turning screen on: " + win); win.mTurnOnScreen = true; } if (win.isConfigChanged()) { if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + win + " visible with new config: " + mCurConfiguration); outConfig.setTo(mCurConfiguration); } } if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) { // To change the format, we need to re-build the surface. winAnimator.destroySurfaceLocked(false); toBeDisplayed = true; surfaceChanged = true; } try { if (!win.mHasSurface) { surfaceChanged = true; } Surface surface = winAnimator.createSurfaceLocked(); if (surface != null) { outSurface.copyFrom(surface); if (SHOW_TRANSACTIONS) Slog.i(TAG, " 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(); } } catch (Exception e) { mInputMonitor.updateInputWindowsLw(true /*force*/); Slog.w(TAG, "Exception thrown when creating surface for client " + client + " (" + win.mAttrs.getTitle() + ")", e); Binder.restoreCallingIdentity(origId); return 0; } if (toBeDisplayed) { focusMayChange = isDefaultDisplay; } if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) { mInputMethodWindow = win; imMayMove = true; } if (win.mAttrs.type == TYPE_BASE_APPLICATION && win.mAppToken != null && win.mAppToken.startingWindow != null) { // Special handling of starting window over the base // window of the app: propagate lock screen flags to it, // to provide the correct semantics while starting. final int mask = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs; sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask); } } else { winAnimator.mEnterAnimationPending = false; if (winAnimator.mSurface != null) { if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win + ": mExiting=" + win.mExiting); // If we are not currently running the exit animation, we // need to see about starting one. if (!win.mExiting) { surfaceChanged = true; // 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.mExiting = true; } else if (win.mWinAnimator.isAnimating()) { // Currently in a hide animation... turn this into // an exit. win.mExiting = true; } else if (win == mWallpaperTarget) { // If the wallpaper is currently behind this // window, we need to change both of them inside // of a transaction to avoid artifacts. win.mExiting = true; win.mWinAnimator.mAnimating = true; } else { if (mInputMethodWindow == win) { mInputMethodWindow = null; } winAnimator.destroySurfaceLocked(false); } scheduleNotifyWindowTranstionIfNeededLocked(win, transit); } } outSurface.release(); if (DEBUG_VISIBILITY) Slog.i(TAG, "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 assignLayers = false; if (imMayMove) { if (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. assignLayers = true; } } if (wallpaperMayMove) { if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { assignLayers = true; } } win.mDisplayContent.layoutNeeded = true; win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; if (assignLayers) { assignLayersLocked(win.getWindowList()); } configChanged = updateOrientationFromAppTokensLocked(false); performLayoutAndPlaceSurfacesLocked(); if (toBeDisplayed && win.mIsWallpaper) { DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); updateWallpaperOffsetLocked(win, displayInfo.appWidth, displayInfo.appHeight, false); } if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } outFrame.set(win.mCompatFrame); outContentInsets.set(win.mContentInsets); outVisibleInsets.set(win.mVisibleInsets); if (localLOGV) Slog.v( TAG, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth + ", requestedHeight=" + requestedHeight + ", viewVisibility=" + viewVisibility + "\nRelayout returning frame=" + outFrame + ", surface=" + outSurface); if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange); inTouchMode = mInTouchMode; animating = mAnimator.mAnimating; if (animating && !mRelayoutWhileAnimating.contains(win)) { mRelayoutWhileAnimating.add(win); } mInputMonitor.updateInputWindowsLw(true /*force*/); if (DEBUG_LAYOUT) { Slog.v(TAG, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString()); } } if (configChanged) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0) | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0) | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0) | (animating ? WindowManagerGlobal.RELAYOUT_RES_ANIMATING : 0); } public void performDeferredDestroyWindow(Session session, IWindow client) { long origId = Binder.clearCallingIdentity(); try { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } win.mWinAnimator.destroyDeferredSurfaceLocked(false); } } 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(); synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win != null && win.mWinAnimator.finishDrawingLocked()) { if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } win.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } Binder.restoreCallingIdentity(origId); } @Override public float getWindowCompatibilityScale(IBinder windowToken) { if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, "getWindowCompatibilityScale()")) { throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); } synchronized (mWindowMap) { WindowState windowState = mWindowMap.get(windowToken); return (windowState != null) ? windowState.mGlobalScale : 1.0f; } } @Override public WindowInfo getWindowInfo(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, "getWindowInfo()")) { throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); } synchronized (mWindowMap) { WindowState window = mWindowMap.get(token); if (window != null) { return getWindowInfoForWindowStateLocked(window); } return null; } } @Override public void getVisibleWindowsForDisplay(int displayId, List outInfos) { if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, "getWindowInfos()")) { throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); } synchronized (mWindowMap) { DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent == null) { return; } WindowList windows = displayContent.getWindowList(); final int windowCount = windows.size(); for (int i = 0; i < windowCount; i++) { WindowState window = windows.get(i); if (window.isVisibleLw() || window.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) { WindowInfo info = getWindowInfoForWindowStateLocked(window); outInfos.add(info); } } } } @Override public void magnifyDisplay(int displayId, float scale, float offsetX, float offsetY) { if (!checkCallingPermission( android.Manifest.permission.MAGNIFY_DISPLAY, "magnifyDisplay()")) { throw new SecurityException("Requires MAGNIFY_DISPLAY permission"); } synchronized (mWindowMap) { MagnificationSpec spec = getDisplayMagnificationSpecLocked(displayId); if (spec != null) { final boolean scaleChanged = spec.mScale != scale; final boolean offsetChanged = spec.mOffsetX != offsetX || spec.mOffsetY != offsetY; if (!scaleChanged && !offsetChanged) { return; } spec.initialize(scale, offsetX, offsetY); // If the offset has changed we need to re-add the input windows // since the offsets have to be propagated to the input system. if (offsetChanged) { // TODO(multidisplay): Input only occurs on the default display. if (displayId == Display.DEFAULT_DISPLAY) { mInputMonitor.updateInputWindowsLw(true); } } scheduleAnimationLocked(); } } } MagnificationSpec getDisplayMagnificationSpecLocked(int displayId) { DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null) { if (displayContent.mMagnificationSpec == null) { displayContent.mMagnificationSpec = new MagnificationSpec(); } return displayContent.mMagnificationSpec; } return null; } private WindowInfo getWindowInfoForWindowStateLocked(WindowState window) { WindowInfo info = WindowInfo.obtain(); info.token = window.mToken.token; info.frame.set(window.mFrame); info.type = window.mAttrs.type; info.displayId = window.getDisplayId(); info.compatibilityScale = window.mGlobalScale; info.visible = window.isVisibleLw() || info.type == TYPE_UNIVERSE_BACKGROUND; info.layer = window.mLayer; window.getTouchableRegion(mTempRegion); mTempRegion.getBounds(info.touchableRegion); return info; } private AttributeCache.Entry getCachedAnimations(int userId, WindowManager.LayoutParams lp) { if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg=" + (lp != null ? lp.packageName : null) + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null)); if (lp != null && lp.windowAnimations != 0) { // If this is a system resource, don't try to load it from the // application resources. It is nice to avoid loading application // resources if we can. String packageName = lp.packageName != null ? lp.packageName : "android"; int resId = lp.windowAnimations; if ((resId&0xFF000000) == 0x01000000) { packageName = "android"; } if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" + packageName); return AttributeCache.instance().get(userId, packageName, resId, com.android.internal.R.styleable.WindowAnimation); } return null; } private AttributeCache.Entry getCachedAnimations(int userId, String packageName, int resId) { if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package=" + packageName + " resId=0x" + Integer.toHexString(resId)); if (packageName != null) { if ((resId&0xFF000000) == 0x01000000) { packageName = "android"; } if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" + packageName); return AttributeCache.instance().get(userId, packageName, resId, com.android.internal.R.styleable.WindowAnimation); } return null; } Animation loadAnimation(int userId, WindowManager.LayoutParams lp, int animAttr) { int anim = 0; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations(userId, lp); if (ent != null) { context = ent.context; anim = ent.array.getResourceId(animAttr, 0); } } if (anim != 0) { return AnimationUtils.loadAnimation(context, anim); } return null; } private Animation loadAnimation(int userId, String packageName, int resId) { int anim = 0; Context context = mContext; if (resId >= 0) { AttributeCache.Entry ent = getCachedAnimations(userId, packageName, resId); if (ent != null) { context = ent.context; anim = resId; } } if (anim != 0) { return AnimationUtils.loadAnimation(context, anim); } return null; } private Animation createExitAnimationLocked(int transit, int duration) { if (transit == WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN || transit == WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE) { // If we are on top of the wallpaper, we need an animation that // correctly handles the wallpaper staying static behind all of // the animated elements. To do this, will just have the existing // element fade out. Animation a = new AlphaAnimation(1, 0); a.setDetachWallpaper(true); a.setDuration(duration); return a; } // For normal animations, the exiting element just holds in place. Animation a = new AlphaAnimation(1, 1); a.setDuration(duration); return a; } /** * Compute the pivot point for an animation that is scaling from a small * rect on screen to a larger rect. The pivot point varies depending on * the distance between the inner and outer edges on both sides. This * function computes the pivot point for one dimension. * @param startPos Offset from left/top edge of outer rectangle to * left/top edge of inner rectangle. * @param finalScale The scaling factor between the size of the outer * and inner rectangles. */ private static float computePivot(int startPos, float finalScale) { final float denom = finalScale-1; if (Math.abs(denom) < .0001f) { return startPos; } return -startPos / denom; } private Animation createScaleUpAnimationLocked(int transit, boolean enter) { Animation a; // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. int duration; switch (transit) { case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: duration = mContext.getResources().getInteger( com.android.internal.R.integer.config_shortAnimTime); break; default: duration = 300; break; } // TODO(multidisplay): For now assume all app animation is on main display. final DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); if (enter) { // Entering app zooms out from the center of the initial rect. float scaleW = mNextAppTransitionStartWidth / (float) displayInfo.appWidth; float scaleH = mNextAppTransitionStartHeight / (float) displayInfo.appHeight; Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, scaleW), computePivot(mNextAppTransitionStartY, scaleH)); scale.setDuration(duration); AnimationSet set = new AnimationSet(true); Animation alpha = new AlphaAnimation(0, 1); scale.setDuration(duration); set.addAnimation(scale); alpha.setDuration(duration); set.addAnimation(alpha); set.setDetachWallpaper(true); a = set; } else { a = createExitAnimationLocked(transit, duration); } a.setFillAfter(true); final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.decelerate_cubic); a.setInterpolator(interpolator); a.initialize(displayInfo.appWidth, displayInfo.appHeight, displayInfo.appWidth, displayInfo.appHeight); return a; } private Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb, boolean scaleUp) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. int duration; switch (transit) { case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: duration = mContext.getResources().getInteger( com.android.internal.R.integer.config_shortAnimTime); break; default: duration = 250; break; } // TOOD(multidisplay): For now assume all app animation is on the main screen. DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); if (thumb) { // Animation for zooming thumbnail from its initial size to // filling the screen. if (scaleUp) { float scaleW = displayInfo.appWidth / thumbWidth; float scaleH = displayInfo.appHeight / thumbHeight; Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, computePivot(mNextAppTransitionStartX, 1 / scaleW), computePivot(mNextAppTransitionStartY, 1 / scaleH)); AnimationSet set = new AnimationSet(false); Animation alpha = new AlphaAnimation(1, 0); scale.setDuration(duration); scale.setInterpolator(AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.decelerate_quad)); set.addAnimation(scale); alpha.setDuration(duration); alpha.setInterpolator(new Interpolator() { @Override public float getInterpolation(float input) { if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) { // linear response return input / RECENTS_THUMBNAIL_FADEOUT_FRACTION; } // complete return 1; } }); set.addAnimation(alpha); set.setFillBefore(true); a = set; } else { float scaleW = displayInfo.appWidth / thumbWidth; float scaleH = displayInfo.appHeight / thumbHeight; Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, 1 / scaleW), computePivot(mNextAppTransitionStartY, 1 / scaleH)); AnimationSet set = new AnimationSet(true); Animation alpha = new AlphaAnimation(1, 1); scale.setDuration(duration); scale.setInterpolator( new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); set.addAnimation(scale); alpha.setDuration(duration); set.addAnimation(alpha); set.setFillBefore(true); a = set; } } else if (enter) { // Entering app zooms out from the center of the thumbnail. if (scaleUp) { float scaleW = thumbWidth / displayInfo.appWidth; float scaleH = thumbHeight / displayInfo.appHeight; Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, scaleW), computePivot(mNextAppTransitionStartY, scaleH)); scale.setDuration(duration); scale.setInterpolator( new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); scale.setFillBefore(true); a = scale; } else { // noop animation a = new AlphaAnimation(1, 1); a.setDuration(duration); } } else { // Exiting app if (scaleUp) { if (transit == WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN) { // Fade out while bringing up selected activity. This keeps the // current activity from showing through a launching wallpaper // activity. a = new AlphaAnimation(1, 0); } else { // noop animation a = new AlphaAnimation(1, 1); } a.setDuration(duration); } else { float scaleW = thumbWidth / displayInfo.appWidth; float scaleH = thumbHeight / displayInfo.appHeight; Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, computePivot(mNextAppTransitionStartX, scaleW), computePivot(mNextAppTransitionStartY, scaleH)); scale.setDuration(duration); scale.setInterpolator( new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); scale.setFillBefore(true); AnimationSet set = new AnimationSet(true); Animation alpha = new AlphaAnimation(1, 0); set.addAnimation(scale); alpha.setDuration(duration); alpha.setInterpolator(new DecelerateInterpolator( THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); set.addAnimation(alpha); set.setFillBefore(true); set.setZAdjustment(Animation.ZORDER_TOP); a = set; } } a.setFillAfter(true); final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.decelerate_quad); a.setInterpolator(interpolator); a.initialize(displayInfo.appWidth, displayInfo.appHeight, displayInfo.appWidth, displayInfo.appHeight); return a; } private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, int transit, boolean enter) { // 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()) { Animation a; boolean initialized = false; if (mNextAppTransitionType == ActivityOptions.ANIM_CUSTOM) { a = loadAnimation(atoken.userId, mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + atoken + " anim=" + a + " nextAppTransition=ANIM_CUSTOM" + " transit=" + transit + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == ActivityOptions.ANIM_SCALE_UP) { a = createScaleUpAnimationLocked(transit, enter); initialized = true; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + atoken + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" + " transit=" + transit + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP || mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN) { boolean scaleUp = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP); a = createThumbnailAnimationLocked(transit, enter, false, scaleUp); initialized = true; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { String animName = scaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; Slog.v(TAG, "applyAnimation: atoken=" + atoken + " anim=" + a + " nextAppTransition=" + animName + " transit=" + transit + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } } else { int animAttr = 0; switch (transit) { case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation; break; case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; break; case WindowManagerPolicy.TRANSIT_TASK_OPEN: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation; break; case WindowManagerPolicy.TRANSIT_TASK_CLOSE: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation; break; case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation; break; case WindowManagerPolicy.TRANSIT_TASK_TO_BACK: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation; break; case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation : com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation; break; case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation : com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation; break; case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; break; case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE: animAttr = enter ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation; break; } a = animAttr != 0 ? loadAnimation(atoken.userId, lp, animAttr) : null; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + atoken + " anim=" + a + " animAttr=0x" + Integer.toHexString(animAttr) + " transit=" + transit + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } if (a != null) { if (DEBUG_ANIM) { RuntimeException e = null; if (!HIDE_STACK_CRAWLS) { e = new RuntimeException(); e.fillInStackTrace(); } Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e); } atoken.mAppAnimator.setAnimation(a, initialized); } } else { atoken.mAppAnimator.clearAnimation(); } return atoken.mAppAnimator.animation != null; } // ------------------------------------------------------------- // Application Window Tokens // ------------------------------------------------------------- public void validateAppTokens(List tokens) { int v = tokens.size()-1; int m = mAppTokens.size()-1; while (v >= 0 && m >= 0) { AppWindowToken atoken = mAppTokens.get(m); if (atoken.removed) { m--; continue; } if (tokens.get(v) != atoken.token) { Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v) + " @ " + v + ", internal is " + atoken.token + " @ " + m); } v--; m--; } while (v >= 0) { Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v); v--; } while (m >= 0) { AppWindowToken atoken = mAppTokens.get(m); if (!atoken.removed) { Slog.w(TAG, "Invalid internal atoken: " + atoken.token + " @ " + m); } m--; } } 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, msg); return false; } boolean okToDisplay() { return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOnFully(); } 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, "Attempted to add existing input method token: " + token); return; } wtoken = new WindowToken(this, token, type, true); mTokenMap.put(token, wtoken); if (type == TYPE_WALLPAPER) { mWallpaperTokens.add(wtoken); updateLayoutToAnimWallpaperTokens(); } } } @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) { 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 || mClosingApps.size() > 0) { // If the display is frozen, some activities may be in the middle // of restarting, and thus have removed their old window. If the // window has the flag to hide the lock screen, then the lock screen // can re-appear and inflict its own orientation on us. Keep the // orientation stable until this all settles down. return mLastWindowForcedOrientation; } // TODO(multidisplay): Change to the correct display. final WindowList windows = getDefaultWindowListLocked(); int pos = windows.size() - 1; while (pos >= 0) { WindowState win = windows.get(pos); 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. return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { continue; } int req = win.mAttrs.screenOrientation; if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ continue; } if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req); return (mLastWindowForcedOrientation=req); } return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } public int getOrientationFromAppTokensLocked() { int curGroup = 0; int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean findingBehind = false; boolean haveGroup = false; boolean lastFullscreen = false; for (int pos = mAppTokens.size() - 1; pos >= 0; pos--) { AppWindowToken atoken = mAppTokens.get(pos); if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "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, "Skipping " + atoken + " -- going to hide"); continue; } if (haveGroup == true && curGroup != atoken.groupId) { // If we have hit a new application group, 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 != ActivityInfo.SCREEN_ORIENTATION_BEHIND && lastFullscreen) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + " -- end of group, return " + lastOrientation); return lastOrientation; } } // We ignore any hidden applications on the top. if (atoken.hiddenRequested || atoken.willBeHidden) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken + " -- hidden on top"); continue; } if (!haveGroup) { haveGroup = true; curGroup = atoken.groupId; 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 != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + " -- full screen, return " + or); return or; } // If this application has requested an explicit orientation, // then use it. if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + " -- explicitly set, return " + or); return or; } findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); } if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation"); return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @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) { Configuration config = null; if (updateOrientationFromAppTokensLocked(false)) { if (freezeThisOneIfNeeded != null) { AppWindowToken atoken = findAppWindowToken( freezeThisOneIfNeeded); if (atoken != null) { startAppFreezingScreenLocked(atoken, ActivityInfo.CONFIG_ORIENTATION); } } 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.fontScale = currentConfig.fontScale; if (computeScreenConfigurationLocked(mTempConfiguration)) { if (currentConfig.diff(mTempConfiguration) != 0) { mWaitingForConfig = true; getDefaultDisplayContentLocked().layoutNeeded = true; startFreezingDisplayLocked(false, 0, 0); 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 = computeForcedAppOrientationLocked(); 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); } } int computeForcedAppOrientationLocked() { int req = getOrientationFromWindowsLocked(); if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { req = getOrientationFromAppTokensLocked(); } return req; } @Override public void setNewConfiguration(Configuration config) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setNewConfiguration()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { mCurConfiguration = new Configuration(config); mWaitingForConfig = false; performLayoutAndPlaceSurfacesLocked(); } } @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, "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; } } @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) { boolean changed = false; if (token == null) { if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp); changed = mFocusedApp != null; mFocusedApp = null; if (changed) { mInputMonitor.setFocusedAppLw(null); } } else { AppWindowToken newFocus = findAppWindowToken(token); if (newFocus == null) { Slog.w(TAG, "Attempted to set focus to non-existing app token: " + token); return; } changed = mFocusedApp != newFocus; mFocusedApp = newFocus; if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp + " moveFocusNow=" + moveFocusNow); if (changed) { mInputMonitor.setFocusedAppLw(newFocus); } } if (moveFocusNow && changed) { final long origId = Binder.clearCallingIdentity(); updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); Binder.restoreCallingIdentity(origId); } } } @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) { if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Prepare app transition: transit=" + transit + " mNextAppTransition=" + mNextAppTransition + " alwaysKeepCurrent=" + alwaysKeepCurrent + " Callers=" + Debug.getCallers(3)); if (okToDisplay()) { if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = transit; } else if (!alwaysKeepCurrent) { if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) { // Opening a new task always supersedes a close for the anim. mNextAppTransition = transit; } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) { // Opening a new activity always supersedes a close for the anim. mNextAppTransition = transit; } } mAppTransitionReady = false; mAppTransitionTimeout = false; mStartingIconInTransition = false; mSkipAppTransitionAnimation = false; mH.removeMessages(H.APP_TRANSITION_TIMEOUT); mH.sendMessageDelayed(mH.obtainMessage(H.APP_TRANSITION_TIMEOUT), 5000); } } } @Override public int getPendingAppTransition() { return mNextAppTransition; } private void scheduleAnimationCallback(IRemoteCallback cb) { if (cb != null) { mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, cb)); } } @Override public void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, IRemoteCallback startedCallback) { synchronized(mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransitionType = ActivityOptions.ANIM_CUSTOM; mNextAppTransitionPackage = packageName; mNextAppTransitionThumbnail = null; mNextAppTransitionEnter = enterAnim; mNextAppTransitionExit = exitAnim; scheduleAnimationCallback(mNextAppTransitionCallback); mNextAppTransitionCallback = startedCallback; } else { scheduleAnimationCallback(startedCallback); } } } @Override public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, int startHeight) { synchronized(mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransitionType = ActivityOptions.ANIM_SCALE_UP; mNextAppTransitionPackage = null; mNextAppTransitionThumbnail = null; mNextAppTransitionStartX = startX; mNextAppTransitionStartY = startY; mNextAppTransitionStartWidth = startWidth; mNextAppTransitionStartHeight = startHeight; scheduleAnimationCallback(mNextAppTransitionCallback); mNextAppTransitionCallback = null; } } } @Override public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp) { synchronized(mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransitionType = scaleUp ? ActivityOptions.ANIM_THUMBNAIL_SCALE_UP : ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; mNextAppTransitionPackage = null; mNextAppTransitionThumbnail = srcThumb; mNextAppTransitionScaleUp = scaleUp; mNextAppTransitionStartX = startX; mNextAppTransitionStartY = startY; scheduleAnimationCallback(mNextAppTransitionCallback); mNextAppTransitionCallback = startedCallback; } else { scheduleAnimationCallback(startedCallback); } } } @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) { RuntimeException e = new RuntimeException("here"); e.fillInStackTrace(); Slog.w(TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition, e); } if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mAppTransitionReady = true; final long origId = Binder.clearCallingIdentity(); performLayoutAndPlaceSurfacesLocked(); Binder.restoreCallingIdentity(origId); } } } @Override public void setAppStartingWindow(IBinder token, String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, 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, "setAppStartingWindow: token=" + token + " pkg=" + pkg + " transferFrom=" + transferFrom); AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null) { Slog.w(TAG, "Attempted to set icon of non-existing app token: " + token); return; } // 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; } if (wtoken.startingData != null) { return; } if (transferFrom != null) { AppWindowToken ttoken = findAppWindowToken(transferFrom); if (ttoken != null) { WindowState startingWindow = ttoken.startingWindow; if (startingWindow != null) { if (mStartingIconInTransition) { // 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, "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; startingWindow.mWinAnimator.mAppAnimator = wtoken.mAppAnimator; if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) { Slog.v(TAG, "Removing starting window: " + startingWindow); } startingWindow.getWindowList().remove(startingWindow); mWindowsChanged = true; if (DEBUG_ADD_REMOVE) Slog.v(TAG, "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; } if (ttoken.firstWindowDrawn) { wtoken.firstWindowDrawn = true; } if (!ttoken.hidden) { wtoken.hidden = false; wtoken.hiddenRequested = false; wtoken.willBeHidden = false; } if (wtoken.clientHidden != ttoken.clientHidden) { wtoken.clientHidden = ttoken.clientHidden; wtoken.sendAppVisibilityToClients(); } final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator; final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator; if (tAppAnimator.animation != null) { wAppAnimator.animation = tAppAnimator.animation; wAppAnimator.animating = tAppAnimator.animating; wAppAnimator.animLayerAdjustment = tAppAnimator.animLayerAdjustment; tAppAnimator.animation = null; tAppAnimator.animLayerAdjustment = 0; wAppAnimator.updateLayers(); tAppAnimator.updateLayers(); } updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); getDefaultDisplayContentLocked().layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); Binder.restoreCallingIdentity(origId); return; } 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, "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; } 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.thumbnailX = tAppAnimator.thumbnailX; wAppAnimator.thumbnailY = tAppAnimator.thumbnailY; wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer; wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation; tAppAnimator.thumbnail = null; } } } // There is no existing starting window, and the caller doesn't // want us to create one, so that's it! if (!createIfNeeded) { return; } // 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, "Checking theme of starting window: 0x" + Integer.toHexString(theme)); if (theme != 0) { AttributeCache.Entry ent = AttributeCache.instance().get(wtoken.userId, pkg, theme, com.android.internal.R.styleable.Window); if (ent == null) { // Whoops! App doesn't exist. Um. Okay. We'll just // pretend like we didn't see that. return; } if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Translucent=" + ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsTranslucent, false) + " Floating=" + ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsFloating, false) + " ShowWallpaper=" + ent.array.getBoolean( com.android.internal.R.styleable.Window_windowShowWallpaper, false)); if (ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsTranslucent, false)) { return; } if (ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsFloating, false)) { return; } if (ent.array.getBoolean( com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { if (mWallpaperTarget == null) { // If this theme is requesting a wallpaper, and the wallpaper // is not curently 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; } } } if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Creating StartingData"); mStartingIconInTransition = true; wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel, labelRes, icon, 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, "Enqueueing ADD_STARTING"); mH.sendMessageAtFrontOfQueue(m); } } public void setAppWillBeHidden(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppWillBeHidden()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; synchronized(mWindowMap) { wtoken = findAppWindowToken(token); if (wtoken == null) { Slog.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token); return; } wtoken.willBeHidden = true; } } boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout) { boolean delayed = false; if (wtoken.clientHidden == visible) { wtoken.clientHidden = !visible; wtoken.sendAppVisibilityToClients(); } wtoken.willBeHidden = false; if (wtoken.hidden == visible) { boolean changed = false; if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); boolean runningAppAnimation = false; if (transit != WindowManagerPolicy.TRANSIT_UNSET) { if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { wtoken.mAppAnimator.animation = null; } if (applyAnimationLocked(wtoken, lp, transit, visible)) { delayed = runningAppAnimation = true; } WindowState window = wtoken.findMainWindow(); if (window != null) { scheduleNotifyWindowTranstionIfNeededLocked(window, transit); } changed = true; } final int N = wtoken.allAppWindows.size(); for (int i=0; i