/* * 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_BLUR_BEHIND; 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_ALT_FOCUSABLE_IM; 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_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 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_WALLPAPER; import com.android.internal.app.IBatteryStats; import com.android.internal.policy.PolicyManager; import com.android.internal.policy.impl.PhoneWindowManager; import com.android.internal.view.BaseInputHandler; 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.PowerManagerService; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; import android.Manifest; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; 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.Region; import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.LocalPowerManager; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Power; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.TokenWatcher; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Display; import android.view.IApplicationToken; 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.InputHandler; import android.view.InputQueue; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; import android.view.WindowManager.LayoutParams; import android.view.animation.Animation; import android.view.animation.AnimationUtils; 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.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { 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_DRAG = false; static final boolean SHOW_SURFACE_ALLOC = false; static final boolean SHOW_TRANSACTIONS = false; static final boolean HIDE_STACK_CRAWLS = true; static final boolean PROFILE_ORIENTATION = false; static final boolean BLUR = true; 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; /** * 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; /** * 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 event injection. // FIXME is this value reasonable? private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; // 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"; /** * Condition waited on by {@link #reenableKeyguard} to know the call to * the window policy has finished. * This is set to true only if mKeyguardTokenWatcher.acquired() has * actually disabled the keyguard. */ private boolean mKeyguardDisabled = false; private static final int ALLOW_DISABLE_YES = 1; private static final int ALLOW_DISABLE_NO = 0; private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher( new Handler(), "WindowManagerService.mKeyguardTokenWatcher") { public void acquired() { if (shouldAllowDisableKeyguard()) { mPolicy.enableKeyguard(false); mKeyguardDisabled = true; } else { Log.v(TAG, "Not disabling keyguard since device policy is enforced"); } } public void released() { mPolicy.enableKeyguard(true); synchronized (mKeyguardTokenWatcher) { mKeyguardDisabled = false; mKeyguardTokenWatcher.notifyAll(); } } }; final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { mPolicy.enableKeyguard(true); synchronized(mKeyguardTokenWatcher) { // lazily evaluate this next time we're asked to disable keyguard mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; mKeyguardDisabled = false; } } }; final Context mContext; final boolean mHaveInputMethods; final boolean mLimitedAlphaCompositing; final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); 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(); /** * Z-ordered (bottom-most first) list of all application tokens, for * controlling the ordering of windows in different applications. This * contains AppWindowToken objects. */ final ArrayList mAppTokens = 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(); /** * Z-ordered (bottom-most first) list of all Window objects. */ final ArrayList mWindows = 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; /** * Used when rebuilding window list to keep track of windows that have * been removed. */ WindowState[] mRebuildTmp = new WindowState[20]; IInputMethodManager mInputMethodManager; SurfaceSession mFxSession; private DimAnimator mDimAnimator = null; Surface mBlurSurface; boolean mBlurShown; Watermark mWatermark; StrictModeFlash mStrictModeFlash; ScreenRotationAnimation mScreenRotationAnimation; BlackFrame mBlackFrame; int mTransactionSequence = 0; final float[] mTmpFloats = new float[9]; boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; int mInitialDisplayWidth = 0; int mInitialDisplayHeight = 0; int mBaseDisplayWidth = 0; int mBaseDisplayHeight = 0; int mCurDisplayWidth = 0; int mCurDisplayHeight = 0; int mAppDisplayWidth = 0; int mAppDisplayHeight = 0; int mRotation = 0; int mRequestedRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; int mLastRotationFlags; ArrayList mRotationWatchers = new ArrayList(); int mDeferredRotation; int mDeferredRotationAnimFlags; boolean mLayoutNeeded = true; boolean mAnimationPending = false; boolean mDisplayFrozen = false; boolean mWaitingForConfig = false; boolean mWindowsFreezingScreen = false; long mFreezeGcPending = 0; int mAppsFreezingScreen = 0; int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int mLayoutSeq = 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; String mNextAppTransitionPackage; int mNextAppTransitionEnter; int mNextAppTransitionExit; 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(); final ArrayList mToTopApps = new ArrayList(); final ArrayList mToBottomApps = new ArrayList(); Display mDisplay; final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics(); H mH = new H(); 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; 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. WindowState mUpperWallpaperTarget = null; // Window currently running an animation that has requested it be detached // from the wallpaper. This means we need to ensure the wallpaper is // visible behind it in case it animates in a way that would allow it to be // seen. WindowState mWindowDetachedWallpaper = null; DimSurface mWindowAnimationBackgroundSurface = 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; final InputManager mInputManager; // Who is holding the screen on. Session mHoldingScreenOn; PowerManager.WakeLock mHoldingScreenWakeLock; boolean mTurnOnScreen; DragState mDragState = null; final InputHandler mDragInputHandler = new BaseInputHandler() { @Override public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { boolean handled = false; try { if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 && mDragState != null) { boolean endDrag = false; final float newX = event.getRawX(); final float newY = event.getRawY(); switch (event.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 { finishedCallback.finished(handled); } } }; /** * Whether the UI is currently running in touch mode (not showing * navigational focus because the user is directly pressing the screen). */ boolean mInTouchMode = false; 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; public static WindowManagerService main(Context context, PowerManagerService pm, boolean haveInputMethods) { WMThread thr = new WMThread(context, pm, haveInputMethods); thr.start(); synchronized (thr) { while (thr.mService == null) { try { thr.wait(); } catch (InterruptedException e) { } } return thr.mService; } } static class WMThread extends Thread { WindowManagerService mService; private final Context mContext; private final PowerManagerService mPM; private final boolean mHaveInputMethods; public WMThread(Context context, PowerManagerService pm, boolean haveInputMethods) { super("WindowManager"); mContext = context; mPM = pm; mHaveInputMethods = haveInputMethods; } public void run() { Looper.prepare(); WindowManagerService s = new WindowManagerService(mContext, mPM, mHaveInputMethods); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DISPLAY); android.os.Process.setCanSelfBackground(false); synchronized (this) { mService = s; notifyAll(); } // For debug builds, log event loop stalls to dropbox for analysis. if (StrictMode.conditionallyEnableDebugLogging()) { Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper"); } Looper.loop(); } } static class PolicyThread extends Thread { private final WindowManagerPolicy mPolicy; private final WindowManagerService mService; private final Context mContext; private final PowerManagerService mPM; boolean mRunning = false; public PolicyThread(WindowManagerPolicy policy, WindowManagerService service, Context context, PowerManagerService pm) { super("WindowManagerPolicy"); mPolicy = policy; mService = service; mContext = context; mPM = pm; } public void run() { Looper.prepare(); WindowManagerPolicyThread.set(this, Looper.myLooper()); //Looper.myLooper().setMessageLogging(new LogPrinter( // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM)); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); android.os.Process.setCanSelfBackground(false); mPolicy.init(mContext, mService, mPM); synchronized (this) { mRunning = true; notifyAll(); } // For debug builds, log event loop stalls to dropbox for analysis. if (StrictMode.conditionallyEnableDebugLogging()) { Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper"); } Looper.loop(); } } private WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods) { mContext = context; mHaveInputMethods = haveInputMethods; mLimitedAlphaCompositing = context.getResources().getBoolean( com.android.internal.R.bool.config_sf_limitedAlpha); 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.System.getFloat(context.getContentResolver(), Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(), Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); // 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, "KEEP_SCREEN_ON_FLAG"); mHoldingScreenWakeLock.setReferenceCounted(false); mInputManager = new InputManager(context, this); PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); thr.start(); synchronized (thr) { while (!thr.mRunning) { try { thr.wait(); } catch (InterruptedException e) { } } } mInputManager.start(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { return super.onTransact(code, data, reply, flags); } catch (RuntimeException e) { // The window manager only throws security exceptions, so let's // log all others. if (!(e instanceof SecurityException)) { Slog.e(TAG, "Window Manager Crash", e); } throw e; } } private void placeWindowAfter(WindowState pos, WindowState window) { final int i = mWindows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " + (i+1) + " of " + mWindows.size() + " (after " + pos + ")"); mWindows.add(i+1, window); mWindowsChanged = true; } private void placeWindowBefore(WindowState pos, WindowState window) { final int i = mWindows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " + i + " of " + mWindows.size() + " (before " + pos + ")"); mWindows.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) { //use a local variable to cache mWindows ArrayList localmWindows = mWindows; int jmax = localmWindows.size(); if(jmax == 0) { return -1; } for(int j = (jmax-1); j >= 0; j--) { WindowState wentry = localmWindows.get(j); if(wentry.mAppToken == win.mAppToken) { return j; } } return -1; } private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { final IWindow client = win.mClient; final WindowToken token = win.mToken; final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); final WindowState attached = win.mAttachedWindow; int i; if (attached == null) { int tokenWindowsPos = token.windows.size(); if (token.appWindowToken != null) { int index = tokenWindowsPos-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. placeWindowBefore(token.windows.get(0), win); tokenWindowsPos = 0; } else { AppWindowToken atoken = win.mAppToken; if (atoken != null && token.windows.get(index) == atoken.startingWindow) { placeWindowBefore(token.windows.get(index), win); tokenWindowsPos--; } else { int newIdx = findIdxBasedOnAppTokens(win); if(newIdx != -1) { //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); } localmWindows.add(newIdx+1, win); mWindowsChanged = true; } } } } else { 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 = mAppTokens.size(); WindowState pos = null; for (i=NA-1; i>=0; i--) { AppWindowToken t = mAppTokens.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, we can // use it as an anchor for when we do reach the token. if (!t.sendingToBottom && t.windows.size() > 0) { pos = t.windows.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) { final int NC = atoken.windows.size(); if (NC > 0) { WindowState bottom = atoken.windows.get(0); if (bottom.mSubLayer < 0) { pos = bottom; } } } placeWindowBefore(pos, win); } else { // Continue looking down until we find the first // token that has windows. while (i >= 0) { AppWindowToken t = mAppTokens.get(i); final int NW = t.windows.size(); if (NW > 0) { pos = t.windows.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); } localmWindows.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 (localmWindows.get(i).mBaseLayer <= myLayer) { i++; break; } } if (i < 0) i = 0; if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + win + " at " + i + " of " + N); localmWindows.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 = token.windows.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); } } 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 == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { if (DEBUG_INPUT_METHOD) { Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding()); if (!w.isVisibleOrAdding()) { Slog.i(TAG, " mSurface=" + w.mSurface + " reportDestroy=" + w.mReportDestroySurface + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility + " policyVis=" + w.mPolicyVisibility + " 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; } int findDesiredInputMethodWindowIndexLocked(boolean willMove) { final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); WindowState w = null; int i = N; while (i > 0) { i--; w = localmWindows.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 == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING && i > 0) { WindowState wb = localmWindows.get(i-1); if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { i--; w = wb; } } break; } } 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. if (mInputMethodTarget != null && w != null && mInputMethodTarget.isDisplayedLw() && mInputMethodTarget.mExiting) { if (mInputMethodTarget.mAnimLayer > w.mAnimLayer) { w = mInputMethodTarget; i = localmWindows.indexOf(w); if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, switching to: " + w); } } if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Desired input method target=" + w + " willMove=" + willMove); if (willMove && w != null) { final WindowState curTarget = mInputMethodTarget; if (curTarget != null && curTarget.mAppToken != 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. AppWindowToken token = curTarget.mAppToken; WindowState highestTarget = null; int highestPos = 0; if (token.animating || token.animation != null) { int pos = 0; pos = localmWindows.indexOf(curTarget); while (pos >= 0) { WindowState win = localmWindows.get(pos); if (win.mAppToken != token) { break; } if (!win.mRemoved) { if (highestTarget == null || win.mAnimLayer > highestTarget.mAnimLayer) { highestTarget = win; highestPos = pos; } } pos--; } } if (highestTarget != null) { if (DEBUG_INPUT_METHOD) Slog.v(TAG, "mNextAppTransition=" + mNextAppTransition + " " + highestTarget + " animating=" + highestTarget.isAnimating() + " layer=" + highestTarget.mAnimLayer + " new layer=" + w.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.isAnimating() && highestTarget.mAnimLayer > w.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) { RuntimeException e = null; if (!HIDE_STACK_CRAWLS) { e = new RuntimeException(); e.fillInStackTrace(); } Slog.w(TAG, "Moving IM target from " + mInputMethodTarget + " to " + w, e); } mInputMethodTarget = w; mInputMethodTargetWaitingAnim = false; if (w.mAppToken != null) { setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment); } else { setInputMethodAnimLayerAdjustment(0); } } return i+1; } if (willMove) { if (DEBUG_INPUT_METHOD) { RuntimeException e = null; if (!HIDE_STACK_CRAWLS) { e = new RuntimeException(); e.fillInStackTrace(); } Slog.w(TAG, "Moving IM target from " + mInputMethodTarget + " to null", e); } 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); mWindows.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.mAnimLayer = imw.mLayer + adj; if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + " anim layer: " + imw.mAnimLayer); int wi = imw.mChildWindows.size(); while (wi > 0) { wi--; WindowState cw = imw.mChildWindows.get(wi); cw.mAnimLayer = cw.mLayer + adj; if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw + " anim layer: " + cw.mAnimLayer); } } int di = mInputMethodDialogs.size(); while (di > 0) { di --; imw = mInputMethodDialogs.get(di); imw.mAnimLayer = imw.mLayer + adj; if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + " anim layer: " + imw.mAnimLayer); } } private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { int wpos = mWindows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win); mWindows.remove(wpos); mWindowsChanged = true; int NC = win.mChildWindows.size(); while (NC > 0) { NC--; WindowState cw = win.mChildWindows.get(NC); int cpos = mWindows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing child at " + cpos + ": " + cw); mWindows.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. int wpos = mWindows.indexOf(win); if (wpos >= 0) { if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos + ": " + win); mWindows.remove(wpos); mWindowsChanged = true; reAddWindowLocked(wpos, win); } } void logWindowList(String prefix) { int N = mWindows.size(); while (N > 0) { N--; Slog.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); } } void moveInputMethodDialogsLocked(int pos) { ArrayList dialogs = mInputMethodDialogs; 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 < mWindows.size()) { WindowState wp = mWindows.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 = mWindows.size(); WindowState firstImWin = imPos < N ? mWindows.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 (!(mWindows.get(pos)).mIsImWindow) { break; } pos++; } pos++; // Now there should be no more input method windows above. while (pos < N) { if ((mWindows.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(" "); } imPos = tmpRemoveWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List after removing with new pos " + imPos + ":"); logWindowList(" "); } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List after moving IM to " + imPos + ":"); logWindowList(" "); } 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(" "); } if (DN > 0) moveInputMethodDialogsLocked(-1);; } else { moveInputMethodDialogsLocked(-1);; } } if (needAssignLayers) { assignLayersLocked(); } return true; } void adjustInputMethodDialogsLocked() { moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); } final boolean isWallpaperVisible(WindowState wallpaperTarget) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target obscured=" + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null) ? wallpaperTarget.mAppToken.animation : null) + " upper=" + mUpperWallpaperTarget + " lower=" + mLowerWallpaperTarget); return (wallpaperTarget != null && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null && wallpaperTarget.mAppToken.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() { int changed = 0; final int dw = mAppDisplayWidth; final int dh = mAppDisplayHeight; // First find top-most window that has asked to be on top of the // wallpaper; all wallpapers go behind it. final ArrayList localmWindows = mWindows; int N = localmWindows.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 = localmWindows.get(i); if ((w.mAttrs.type == WindowManager.LayoutParams.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.animation == null) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping not hidden or animating token: " + w); continue; } } if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": readyfordisplay=" + w.isReadyForDisplay() + " drawpending=" + w.mDrawPending + " commitdrawpending=" + w.mCommitDrawPending); if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay() && (mWallpaperTarget == w || (!w.mDrawPending && !w.mCommitDrawPending))) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper activity: #" + i + "=" + w); foundW = w; foundI = i; if (w == mWallpaperTarget && ((w.mAppToken != null && w.mAppToken.animation != null) || w.mAnimation != null)) { // 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) 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) Slog.v(TAG, "Wallpaper not changing: waiting for app anim in current target"); return 0; } if (foundW != null && foundW.mAppToken != null) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper not changing: waiting for app anim in found target"); return 0; } } if (mWallpaperTarget != foundW) { if (DEBUG_WALLPAPER) { Slog.v(TAG, "New wallpaper target: " + foundW + " oldTarget: " + mWallpaperTarget); } mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; WindowState oldW = mWallpaperTarget; mWallpaperTarget = foundW; // 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.mAnimation != null || (oldW.mAppToken != null && oldW.mAppToken.animation != null); boolean foundAnim = foundW.mAnimation != null || (foundW.mAppToken != null && foundW.mAppToken.animation != null); if (DEBUG_WALLPAPER) { Slog.v(TAG, "New animation: " + foundAnim + " old animation: " + oldAnim); } if (foundAnim && oldAnim) { int oldI = localmWindows.indexOf(oldW); if (DEBUG_WALLPAPER) { Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); } if (oldI >= 0) { if (DEBUG_WALLPAPER) { 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) { Slog.v(TAG, "Old wallpaper still the target."); } mWallpaperTarget = oldW; } // Now set the upper and lower wallpaper targets // correctly, and make sure that we are positioning // the wallpaper below the lower. if (foundI > oldI) { // The new target is on top of the old one. if (DEBUG_WALLPAPER) { 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) { 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.mAnimation != null || (mLowerWallpaperTarget.mAppToken != null && mLowerWallpaperTarget.mAppToken.animation != null); boolean upperAnimating = mUpperWallpaperTarget.mAnimation != null || (mUpperWallpaperTarget.mAppToken != null && mUpperWallpaperTarget.mAppToken.animation != null); if (!lowerAnimating || !upperAnimating) { if (DEBUG_WALLPAPER) { Slog.v(TAG, "No longer animating wallpaper targets!"); } mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; } } 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.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 = localmWindows.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 ? localmWindows.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) { changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED; token.hidden = !visible; // Need to do a layout to ensure the wallpaper now has the // correct size. mLayoutNeeded = 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. if (wallpaper.mWallpaperVisible != visible) { wallpaper.mWallpaperVisible = visible; try { if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG, "Setting visibility of wallpaper " + wallpaper + ": " + visible); wallpaper.mClient.dispatchAppVisibility(visible); } catch (RemoteException e) { } } wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment; if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper win " + wallpaper + " anim layer: " + wallpaper.mAnimLayer); // First, if this window is at the current index, then all // is well. if (wallpaper == foundW) { foundI--; foundW = foundI > 0 ? localmWindows.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 = localmWindows.indexOf(wallpaper); if (oldIndex >= 0) { if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " + oldIndex + ": " + wallpaper); localmWindows.remove(oldIndex); mWindowsChanged = true; if (oldIndex < foundI) { foundI--; } } // Now stick it in. if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { Slog.v(TAG, "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + foundI); } localmWindows.add(foundI, wallpaper); mWindowsChanged = true; changed |= ADJUST_WALLPAPER_LAYERS_CHANGED; } } 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.mAnimLayer = wallpaper.mLayer + adj; if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper win " + wallpaper + " anim layer: " + wallpaper.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) { 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(); } } } boolean updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { final int dw = mAppDisplayWidth; final int dh = mAppDisplayHeight; boolean changed = false; 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)) { wallpaper.computeShownFrameLocked(); changed = true; // We only want to be synchronous with one wallpaper. sync = false; } } } return changed; } void updateWallpaperVisibilityLocked() { final boolean visible = isWallpaperVisible(mWallpaperTarget); final int dw = mAppDisplayWidth; final int dh = mAppDisplayHeight; 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. mLayoutNeeded = true; } int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); if (visible) { updateWallpaperOffsetLocked(wallpaper, dw, dh, false); } if (wallpaper.mWallpaperVisible != visible) { wallpaper.mWallpaperVisible = visible; try { if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG, "Updating visibility of wallpaper " + wallpaper + ": " + visible); wallpaper.mClient.dispatchAppVisibility(visible); } catch (RemoteException e) { } } } } } public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { int res = mPolicy.checkAddPermission(attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; } boolean reportNewConfig = false; WindowState attachedWindow = null; WindowState win = null; long origId; synchronized(mWindowMap) { if (mDisplay == null) { throw new IllegalStateException("Display has not been initialialized"); } if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "Window " + client + " is already added"); return WindowManagerImpl.ADD_DUPLICATE_ADD; } if (attrs.type >= FIRST_SUB_WINDOW && attrs.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 WindowManagerImpl.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 WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } } boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.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 WindowManagerImpl.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { Slog.w(TAG, "Attempted to add window with exiting application token " + token + ". Aborting."); return WindowManagerImpl.ADD_APP_EXITING; } if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; } } else if (attrs.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 WindowManagerImpl.ADD_BAD_APP_TOKEN; } } else if (attrs.type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } } win = new WindowState(this, session, client, token, attachedWindow, attrs, viewVisibility); 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 WindowManagerImpl.ADD_APP_EXITING; } mPolicy.adjustWindowParamsLw(win.mAttrs); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; } if (outInputChannel != null) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.mInputChannel = inputChannels[0]; inputChannels[1].transferToBinderOutParameter(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); } // From now on, no exceptions or errors allowed! res = WindowManagerImpl.ADD_OKAY; origId = Binder.clearCallingIdentity(); if (addToken) { mTokenMap.put(attrs.token, token); } win.attach(); mWindowMap.put(client.asBinder(), win); if (attrs.type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; } boolean imMayMove = true; if (attrs.type == TYPE_INPUT_METHOD) { mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); adjustInputMethodDialogsLocked(); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (attrs.type == TYPE_WALLPAPER) { mLastWallpaperTimeoutTime = 0; adjustWallpaperWindowsLocked(); } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } } win.mEnterAnimationPending = true; mPolicy.getContentInsetHintLw(attrs, outContentInsets); if (mInTouchMode) { res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; } if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerImpl.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(); // 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.mSurface); final long origId = Binder.clearCallingIdentity(); win.disposeInputChannel(); if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Remove " + win + ": mSurface=" + win.mSurface + " mExiting=" + win.mExiting + " isAnimating=" + win.isAnimating() + " app-animation=" + (win.mAppToken != null ? win.mAppToken.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.mSurface != null && !mDisplayFrozen && mPolicy.isScreenOn()) { // If we are not currently running the exit animation, we // need to see about starting one. if (wasVisible=win.isWinVisibleLw()) { int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } // Try starting an animation. if (applyAnimationLocked(win, transit, false)) { win.mExiting = true; } } if (win.mExiting || win.isAnimating()) { // The exit animation is running... wait for it! //Slog.i(TAG, "*** Running exit animation..."); win.mExiting = true; win.mRemoveOnExit = true; mLayoutNeeded = 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()); mWindows.remove(win); mPendingRemove.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) { 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. 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(); mLayoutNeeded = 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 " + Integer.toHexString(w.hashCode()) + ": " + msg + " / " + w.mAttrs.getTitle(); if (where != null) { Slog.i(TAG, str, where); } else { Slog.i(TAG, str); } } 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.mSurface != null)) { if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion"); Surface.openTransaction(); try { if (SHOW_TRANSACTIONS) logSurface(w, "transparentRegionHint=" + region, null); w.mSurface.setTransparentRegionHint(region); } finally { Surface.closeTransaction(); if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setTransparentRegion"); } } } } 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); } mLayoutNeeded = 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; if (updateWallpaperOffsetLocked(window, true)) { performLayoutAndPlaceSurfacesLocked(); } } } 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 int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { boolean displayed = false; boolean inTouchMode; boolean configChanged; // if they don't have this permission, mask out the status bar bits if (attrs != null) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { attrs.systemUiVisibility &= ~StatusBarManager.DISABLE_MASK; attrs.subtreeSystemUiVisibility &= ~StatusBarManager.DISABLE_MASK; } } long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } win.mRequestedWidth = requestedWidth; win.mRequestedHeight = requestedHeight; if (attrs != null) { mPolicy.adjustWindowParamsLw(attrs); } int attrChanges = 0; int flagChanges = 0; if (attrs != null) { flagChanges = win.mAttrs.flags ^= attrs.flags; attrChanges = win.mAttrs.copyFrom(attrs); } if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": " + win.mAttrs); win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { win.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&( WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0; boolean focusMayChange = win.mViewVisibility != viewVisibility || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) || (!win.mRelayoutCalled); boolean wallpaperMayMove = win.mViewVisibility != viewVisibility && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; win.mRelayoutCalled = true; final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { displayed = !win.isVisibleLw(); if (win.mExiting) { win.mExiting = false; if (win.mAnimation != null) { win.mAnimation.cancel(); win.mAnimation = null; } } if (win.mDestroying) { win.mDestroying = false; mDestroySurface.remove(win); } if (oldVisibility == View.GONE) { win.mEnterAnimationPending = true; } if (displayed) { if (win.mSurface != null && !win.mDrawPending && !win.mCommitDrawPending && !mDisplayFrozen && mPolicy.isScreenOn()) { applyEnterAnimationLocked(win); } 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; } int diff = 0; if (win.mConfiguration != mCurConfiguration && (win.mConfiguration == null || (diff=mCurConfiguration.diff(win.mConfiguration)) != 0)) { win.mConfiguration = mCurConfiguration; if (DEBUG_CONFIGURATION) { Slog.i(TAG, "Window " + win + " visible with new config: " + win.mConfiguration + " / 0x" + Integer.toHexString(diff)); } outConfig.setTo(mCurConfiguration); } } if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) { // To change the format, we need to re-build the surface. win.destroySurfaceLocked(); displayed = true; } try { Surface surface = win.createSurfaceLocked(); if (surface != null) { outSurface.copyFrom(surface); win.mReportDestroySurface = false; win.mSurfacePendingDestroy = false; 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 (displayed) { focusMayChange = true; } 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 { win.mEnterAnimationPending = false; if (win.mSurface != null) { if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win + ": mExiting=" + win.mExiting + " mSurfacePendingDestroy=" + win.mSurfacePendingDestroy); // If we are not currently running the exit animation, we // need to see about starting one. if (!win.mExiting || win.mSurfacePendingDestroy) { // Try starting an animation; if there isn't one, we // can destroy the surface right away. int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() && applyAnimationLocked(win, transit, false)) { focusMayChange = true; win.mExiting = true; } else if (win.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.mAnimating = true; } else { if (mInputMethodWindow == win) { mInputMethodWindow = null; } win.destroySurfaceLocked(); } } } if (win.mSurface == null || (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0 || win.mSurfacePendingDestroy) { // We are being called from a local process, which // means outSurface holds its current surface. Ensure the // surface object is cleared, but we don't want it actually // destroyed at this point. win.mSurfacePendingDestroy = false; outSurface.release(); if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win); } else if (win.mSurface != null) { if (DEBUG_VISIBILITY) Slog.i(TAG, "Keeping surface, will report destroy: " + win); win.mReportDestroySurface = true; outSurface.copyFrom(win.mSurface); } } 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) || displayed) { // 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; } } mLayoutNeeded = true; win.mGivenInsetsPending = insetsPending; if (assignLayers) { assignLayersLocked(); } configChanged = updateOrientationFromAppTokensLocked(false); performLayoutAndPlaceSurfacesLocked(); if (displayed && win.mIsWallpaper) { updateWallpaperOffsetLocked(win, mAppDisplayWidth, mAppDisplayHeight, 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; mInputMonitor.updateInputWindowsLw(true /*force*/); } if (configChanged) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); } 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, "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.finishDrawingLocked()) { if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } Binder.restoreCallingIdentity(origId); } private AttributeCache.Entry getCachedAnimations(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(packageName, resId, com.android.internal.R.styleable.WindowAnimation); } return null; } private AttributeCache.Entry getCachedAnimations(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(packageName, resId, com.android.internal.R.styleable.WindowAnimation); } return null; } void applyEnterAnimationLocked(WindowState win) { int transit = WindowManagerPolicy.TRANSIT_SHOW; if (win.mEnterAnimationPending) { win.mEnterAnimationPending = false; transit = WindowManagerPolicy.TRANSIT_ENTER; } applyAnimationLocked(win, transit, true); } boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. return true; } // 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 (!mDisplayFrozen && mPolicy.isScreenOn()) { int anim = mPolicy.selectAnimationLw(win, transit); int attr = -1; Animation a = null; if (anim != 0) { a = AnimationUtils.loadAnimation(mContext, anim); } else { switch (transit) { case WindowManagerPolicy.TRANSIT_ENTER: attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation; break; case WindowManagerPolicy.TRANSIT_EXIT: attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation; break; case WindowManagerPolicy.TRANSIT_SHOW: attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation; break; case WindowManagerPolicy.TRANSIT_HIDE: attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation; break; } if (attr >= 0) { a = loadAnimation(win.mAttrs, attr); } } if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + win + " anim=" + anim + " attr=0x" + Integer.toHexString(attr) + " mAnimation=" + win.mAnimation + " isEntrance=" + isEntrance); 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 " + win, e); } win.setAnimation(a); win.mAnimationIsEntrance = isEntrance; } } else { win.clearAnimation(); } return win.mAnimation != null; } private Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) { int anim = 0; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations(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(String packageName, int resId) { int anim = 0; Context context = mContext; if (resId >= 0) { AttributeCache.Entry ent = getCachedAnimations(packageName, resId); if (ent != null) { context = ent.context; anim = resId; } } if (anim != 0) { return AnimationUtils.loadAnimation(context, anim); } return null; } private boolean applyAnimationLocked(AppWindowToken wtoken, 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 (!mDisplayFrozen && mPolicy.isScreenOn()) { Animation a; if (mNextAppTransitionPackage != null) { a = loadAnimation(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); } 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(lp, animAttr) : null; if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: wtoken=" + wtoken + " anim=" + a + " animAttr=0x" + Integer.toHexString(animAttr) + " transit=" + transit); } 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 " + wtoken, e); } wtoken.setAnimation(a); } } else { wtoken.clearAnimation(); } return wtoken.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 wtoken = mAppTokens.get(m); if (wtoken.removed) { m--; continue; } if (tokens.get(v) != wtoken.token) { Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v) + " @ " + v + ", internal is " + wtoken.token + " @ " + m); } v--; m--; } while (v >= 0) { Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v); v--; } while (m >= 0) { AppWindowToken wtoken = mAppTokens.get(m); if (!wtoken.removed) { Slog.w(TAG, "Invalid internal token: " + wtoken.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; } AppWindowToken findAppWindowToken(IBinder token) { WindowToken wtoken = mTokenMap.get(token); if (wtoken == null) { return null; } return wtoken.appWindowToken; } 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); } } } 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) { wtoken.hidden = true; 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; } int pos = mWindows.size() - 1; while (pos >= 0) { WindowState wtoken = mWindows.get(pos); pos--; if (wtoken.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 (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) { continue; } int req = wtoken.mAttrs.screenOrientation; if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ continue; } else { return (mLastWindowForcedOrientation=req); } } return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } public int getOrientationFromAppTokensLocked() { int pos = mAppTokens.size() - 1; int curGroup = 0; int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean findingBehind = false; boolean haveGroup = false; boolean lastFullscreen = false; while (pos >= 0) { AppWindowToken wtoken = mAppTokens.get(pos); pos--; if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken); // if we're about to tear down this window and not seek for // the behind activity, don't use it for orientation if (!findingBehind && (!wtoken.hidden && wtoken.hiddenRequested)) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken + " -- going to hide"); continue; } if (!haveGroup) { // We ignore any hidden applications on the top. if (wtoken.hiddenRequested || wtoken.willBeHidden) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken + " -- hidden on top"); continue; } haveGroup = true; curGroup = wtoken.groupId; lastOrientation = wtoken.requestedOrientation; } else if (curGroup != wtoken.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 " + wtoken + " -- end of group, return " + lastOrientation); return lastOrientation; } } int or = wtoken.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 = wtoken.appFullscreen; if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + " -- 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 " + wtoken + " -- 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; } 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 wtoken = findAppWindowToken( freezeThisOneIfNeeded); if (wtoken != null) { startAppFreezingScreenLocked(wtoken, 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 (computeNewConfigurationLocked(mTempConfiguration)) { if (currentConfig.diff(mTempConfiguration) != 0) { mWaitingForConfig = true; mLayoutNeeded = true; startFreezingDisplayLocked(false); 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) { boolean changed = false; 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 (setRotationUncheckedLocked(WindowManagerPolicy.USE_LAST_ROTATION, mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE, inTransaction)) { changed = true; } } return changed; } finally { Binder.restoreCallingIdentity(ident); } } int computeForcedAppOrientationLocked() { int req = getOrientationFromWindowsLocked(); if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { req = getOrientationFromAppTokensLocked(); } return req; } 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(); } } 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 wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { Slog.w(TAG, "Attempted to set orientation of non-existing app token: " + token); return; } wtoken.requestedOrientation = requestedOrientation; } } public int getAppOrientation(IApplicationToken token) { synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } return wtoken.requestedOrientation; } } 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); if (changed) { mInputMonitor.setFocusedAppLw(newFocus); } } if (moveFocusNow && changed) { final long origId = Binder.clearCallingIdentity(); updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); Binder.restoreCallingIdentity(origId); } } } 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); if (!mDisplayFrozen && mPolicy.isScreenOn()) { 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); } } } public int getPendingAppTransition() { return mNextAppTransition; } public void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransitionPackage = packageName; mNextAppTransitionEnter = enterAnim; mNextAppTransitionExit = exitAnim; } } 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); } } } 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, "setAppStartingIcon()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Slog.v( TAG, "setAppStartingIcon: 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 (mDisplayFrozen || !mPolicy.isScreenOn()) { 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 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.startingWindow = startingWindow; ttoken.startingData = null; ttoken.startingView = null; ttoken.startingWindow = null; ttoken.startingMoved = true; startingWindow.mToken = wtoken; startingWindow.mRootToken = wtoken; startingWindow.mAppToken = wtoken; if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing starting window: " + startingWindow); mWindows.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(); } if (ttoken.animation != null) { wtoken.animation = ttoken.animation; wtoken.animating = ttoken.animating; wtoken.animLayerAdjustment = ttoken.animLayerAdjustment; ttoken.animation = null; ttoken.animLayerAdjustment = 0; wtoken.updateLayers(); ttoken.updateLayers(); } updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); mLayoutNeeded = 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; } } } // 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 or wallpaper 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 (theme != 0) { AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, com.android.internal.R.styleable.Window); 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)) { return; } } 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. 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) { final int N = wtoken.allAppWindows.size(); 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.animation == sDummyAnimation) { wtoken.animation = null; } applyAnimationLocked(wtoken, lp, transit, visible); changed = true; if (wtoken.animation != null) { delayed = runningAppAnimation = true; } } for (int i=0; i