diff options
Diffstat (limited to 'services/core/java/com/android/server/wm/WindowManagerService.java')
-rw-r--r-- | services/core/java/com/android/server/wm/WindowManagerService.java | 10827 |
1 files changed, 10827 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java new file mode 100644 index 0000000..04129e2 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -0,0 +1,10827 @@ +/* + * 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.*; + +import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; + +import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + +import android.app.AppOpsManager; +import android.util.TimeUtils; +import android.view.IWindowId; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.policy.PolicyManager; +import com.android.internal.policy.impl.PhoneWindowManager; +import com.android.internal.util.FastPrintWriter; +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethodClient; +import com.android.internal.view.IInputMethodManager; +import com.android.internal.view.WindowManagerPolicyThread; +import com.android.server.AttributeCache; +import com.android.server.EventLogTags; +import com.android.server.UiThread; +import com.android.server.Watchdog; +import com.android.server.am.BatteryStatsService; +import com.android.server.display.DisplayManagerService; +import com.android.server.input.InputManagerService; +import com.android.server.power.PowerManagerService; +import com.android.server.power.ShutdownThread; + +import android.Manifest; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.StatusBarManager; +import android.app.admin.DevicePolicyManager; +import android.animation.ValueAnimator; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.hardware.display.DisplayManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Debug; +import android.os.Handler; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.Trace; +import android.os.WorkSource; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.FloatMath; +import android.util.Log; +import android.util.SparseArray; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseIntArray; +import android.util.TypedValue; +import android.view.Choreographer; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.IApplicationToken; +import android.view.IInputFilter; +import android.view.IMagnificationCallbacks; +import android.view.IOnKeyguardExitResult; +import android.view.IRotationWatcher; +import android.view.IWindow; +import android.view.IWindowManager; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.KeyEvent; +import android.view.MagnificationSpec; +import android.view.MotionEvent; +import android.view.Surface.OutOfResourcesException; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceSession; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.view.WindowManagerPolicy; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerPolicy.FakeWindow; +import android.view.WindowManagerPolicy.PointerEventListener; +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.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +/** {@hide} */ +public class WindowManagerService extends IWindowManager.Stub + implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs, + DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener { + static final String TAG = "WindowManager"; + static final boolean DEBUG = false; + static final boolean DEBUG_ADD_REMOVE = false; + static final boolean DEBUG_FOCUS = false; + static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false; + static final boolean DEBUG_ANIM = false; + static final boolean DEBUG_LAYOUT = false; + static final boolean DEBUG_RESIZE = false; + static final boolean DEBUG_LAYERS = false; + static final boolean DEBUG_INPUT = false; + static final boolean DEBUG_INPUT_METHOD = false; + static final boolean DEBUG_VISIBILITY = false; + static final boolean DEBUG_WINDOW_MOVEMENT = false; + static final boolean DEBUG_TOKEN_MOVEMENT = false; + static final boolean DEBUG_ORIENTATION = false; + static final boolean DEBUG_APP_ORIENTATION = false; + static final boolean DEBUG_CONFIGURATION = false; + static final boolean DEBUG_APP_TRANSITIONS = false; + static final boolean DEBUG_STARTING_WINDOW = false; + static final boolean DEBUG_REORDER = false; + static final boolean DEBUG_WALLPAPER = false; + static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER; + static final boolean DEBUG_DRAG = false; + static final boolean DEBUG_SCREEN_ON = false; + static final boolean DEBUG_SCREENSHOT = false; + static final boolean DEBUG_BOOT = false; + static final boolean DEBUG_LAYOUT_REPEATS = true; + static final boolean DEBUG_SURFACE_TRACE = false; + static final boolean DEBUG_WINDOW_TRACE = false; + static final boolean DEBUG_TASK_MOVEMENT = false; + static final boolean DEBUG_STACK = false; + static final boolean SHOW_SURFACE_ALLOC = false; + static final boolean SHOW_TRANSACTIONS = false; + static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS; + static final boolean HIDE_STACK_CRAWLS = true; + static final int LAYOUT_REPEAT_THRESHOLD = 4; + + static final boolean PROFILE_ORIENTATION = false; + static final boolean localLOGV = DEBUG; + + /** How much to multiply the policy's type layer, to reserve room + * for multiple windows of the same type and Z-ordering adjustment + * with TYPE_LAYER_OFFSET. */ + static final int TYPE_LAYER_MULTIPLIER = 10000; + + /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above + * or below others in the same layer. */ + static final int TYPE_LAYER_OFFSET = 1000; + + /** How much to increment the layer for each window, to reserve room + * for effect surfaces between them. + */ + static final int WINDOW_LAYER_MULTIPLIER = 5; + + /** + * Dim surface layer is immediately below target window. + */ + static final int LAYER_OFFSET_DIM = 1; + + /** + * Blur surface layer is immediately below dim layer. + */ + static final int LAYER_OFFSET_BLUR = 2; + + /** + * FocusedStackFrame layer is immediately above focused window. + */ + static final int LAYER_OFFSET_FOCUSED_STACK = 1; + + /** + * Animation thumbnail is as far as possible below the window above + * the thumbnail (or in other words as far as possible above the window + * below it). + */ + static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER-1; + + /** + * 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 fade-in-out transition for + * compatible windows. + */ + static final int DEFAULT_FADE_IN_OUT_DURATION = 400; + + /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */ + static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000; + + /** Amount of time (in milliseconds) to delay before declaring a starting window leaked. */ + static final int STARTING_WINDOW_TIMEOUT_DURATION = 10000; + + /** + * If true, the window manager will do its own custom freezing and general + * management of the screen during rotation. + */ + static final boolean CUSTOM_SCREEN_ROTATION = true; + + // Maximum number of milliseconds to wait for input devices to be enumerated before + // proceding with safe mode detection. + private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; + + // Default input dispatching timeout in nanoseconds. + static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; + + /** Minimum value for createStack and resizeStack weight value */ + public static final float STACK_WEIGHT_MIN = 0.2f; + + /** Maximum value for createStack and resizeStack weight value */ + public static final float STACK_WEIGHT_MAX = 0.8f; + + static final int UPDATE_FOCUS_NORMAL = 0; + static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; + static final int UPDATE_FOCUS_PLACING_SURFACES = 2; + static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; + + private static final String SYSTEM_SECURE = "ro.secure"; + private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; + + private static final String DENSITY_OVERRIDE = "ro.config.density_override"; + private static final String SIZE_OVERRIDE = "ro.config.size_override"; + + private static final int MAX_SCREENSHOT_RETRIES = 3; + + final private KeyguardDisableHandler mKeyguardDisableHandler; + + final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { + mKeyguardDisableHandler.sendEmptyMessage( + KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED); + } + } + }; + + // Current user when multi-user is enabled. Don't show windows of non-current user. + int mCurrentUserId; + + final Context mContext; + + final boolean mHaveInputMethods; + + final boolean mAllowBootMessages; + + final boolean mLimitedAlphaCompositing; + + final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); + + final IActivityManager mActivityManager; + + final IBatteryStats mBatteryStats; + + final AppOpsManager mAppOps; + + final DisplaySettings mDisplaySettings; + + /** + * All currently active sessions with clients. + */ + final HashSet<Session> mSessions = new HashSet<Session>(); + + /** + * Mapping from an IWindow IBinder to the server's Window object. + * This is also used as the lock for all of our state. + * NOTE: Never call into methods that lock ActivityManagerService while holding this object. + */ + final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>(); + + /** + * Mapping from a token IBinder to a WindowToken object. + */ + final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>(); + + /** + * List of window tokens that have finished starting their application, + * and now need to have the policy remove their windows. + */ + final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>(); + + /** + * Fake windows added to the window manager. Note: ordered from top to + * bottom, opposite of mWindows. + */ + final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<FakeWindowImpl>(); + + /** + * Windows that are being resized. Used so we can tell the client about + * the resize after closing the transaction in which we resized the + * underlying surface. + */ + final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>(); + + /** + * Windows whose animations have ended and now must be removed. + */ + final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>(); + + /** + * Used when processing mPendingRemove to avoid working on the original array. + */ + WindowState[] mPendingRemoveTmp = new WindowState[20]; + + /** + * Windows whose surface should be destroyed. + */ + final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>(); + + /** + * Windows that have lost input focus and are waiting for the new + * focus window to be displayed before they are told about this. + */ + ArrayList<WindowState> mLosingFocus = new ArrayList<WindowState>(); + + /** + * 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<WindowState> mForceRemoves; + + /** + * Windows that clients are waiting to have drawn. + */ + ArrayList<Pair<WindowState, IRemoteCallback>> mWaitingForDrawn + = new ArrayList<Pair<WindowState, IRemoteCallback>>(); + + /** + * Windows that have called relayout() while we were running animations, + * so we need to tell when the animation is done. + */ + final ArrayList<WindowState> mRelayoutWhileAnimating = new ArrayList<WindowState>(); + + /** + * Used when rebuilding window list to keep track of windows that have + * been removed. + */ + WindowState[] mRebuildTmp = new WindowState[20]; + + IInputMethodManager mInputMethodManager; + + DisplayMagnifier mDisplayMagnifier; + + final SurfaceSession mFxSession; + Watermark mWatermark; + StrictModeFlash mStrictModeFlash; + FocusedStackFrame mFocusedStackFrame; + + int mFocusedStackLayer; + + final float[] mTmpFloats = new float[9]; + final Rect mTmpContentRect = new Rect(); + + boolean mDisplayReady; + boolean mSafeMode; + boolean mDisplayEnabled = false; + boolean mSystemBooted = false; + boolean mForceDisplayEnabled = false; + boolean mShowingBootMessages = false; + + String mLastANRState; + + /** All DisplayContents in the world, kept here */ + SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(2); + + int mRotation = 0; + int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + boolean mAltOrientation = false; + ArrayList<IRotationWatcher> mRotationWatchers + = new ArrayList<IRotationWatcher>(); + int mDeferredRotationPauseCount; + + int mSystemDecorLayer = 0; + final Rect mScreenRect = new Rect(); + + boolean mTraversalScheduled = false; + boolean mDisplayFrozen = false; + long mDisplayFreezeTime = 0; + int mLastDisplayFreezeDuration = 0; + Object mLastFinishedFreezeSource = null; + boolean mWaitingForConfig = false; + boolean mWindowsFreezingScreen = false; + boolean mClientFreezingScreen = false; + int mAppsFreezingScreen = 0; + int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + + int mLayoutSeq = 0; + + int mLastStatusBarVisibility = 0; + + // State while inside of layoutAndPlaceSurfacesLocked(). + boolean mFocusMayChange; + + Configuration mCurConfiguration = new Configuration(); + + // This is held as long as we have the screen frozen, to give us time to + // perform a rotation animation when turning off shows the lock screen which + // changes the orientation. + private final PowerManager.WakeLock mScreenFrozenLock; + + final AppTransition mAppTransition; + boolean mStartingIconInTransition = false; + boolean mSkipAppTransitionAnimation = false; + + final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>(); + final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>(); + + boolean mIsTouchDevice; + + final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics(); + final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics(); + final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics(); + + final H mH = new H(); + + final Choreographer mChoreographer = Choreographer.getInstance(); + + WindowState mCurrentFocus = null; + WindowState mLastFocus = null; + + /** This just indicates the window the input method is on top of, not + * necessarily the window its input is going to. */ + WindowState mInputMethodTarget = null; + + /** If true hold off on modifying the animation layer of mInputMethodTarget */ + boolean mInputMethodTargetWaitingAnim; + int mInputMethodAnimLayerAdjustment; + + WindowState mInputMethodWindow = null; + final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); + + boolean mHardKeyboardAvailable; + boolean mHardKeyboardEnabled; + OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; + + final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>(); + + // 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; + 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; + boolean mAnimateWallpaperWithTarget; + + AppWindowToken mFocusedApp = null; + + PowerManagerService mPowerManager; + + float mWindowAnimationScale = 1.0f; + float mTransitionAnimationScale = 1.0f; + float mAnimatorDurationScale = 1.0f; + + final InputManagerService mInputManager; + final DisplayManagerService mDisplayManagerService; + final DisplayManager mDisplayManager; + + // Who is holding the screen on. + Session mHoldingScreenOn; + PowerManager.WakeLock mHoldingScreenWakeLock; + + boolean mTurnOnScreen; + + DragState mDragState = null; + + // For frozen screen animations. + int mExitAnimId, mEnterAnimId; + + /** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple + * methods. */ + class LayoutFields { + static final int SET_UPDATE_ROTATION = 1 << 0; + static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1; + static final int SET_FORCE_HIDING_CHANGED = 1 << 2; + static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3; + static final int SET_TURN_ON_SCREEN = 1 << 4; + static final int SET_WALLPAPER_ACTION_PENDING = 1 << 5; + + boolean mWallpaperForceHidingChanged = false; + boolean mWallpaperMayChange = false; + boolean mOrientationChangeComplete = true; + Object mLastWindowFreezeSource = null; + private Session mHoldScreen = null; + private boolean mObscured = false; + private boolean mSyswin = false; + private float mScreenBrightness = -1; + private float mButtonBrightness = -1; + private long mUserActivityTimeout = -1; + private boolean mUpdateRotation = false; + boolean mWallpaperActionPending = false; + + // Set to true when the display contains content to show the user. + // When false, the display manager may choose to mirror or blank the display. + boolean mDisplayHasContent = false; + + // Only set while traversing the default display based on its content. + // Affects the behavior of mirroring on secondary displays. + boolean mObscureApplicationContentOnSecondaryDisplays = false; + } + final LayoutFields mInnerFields = new LayoutFields(); + + boolean mAnimationScheduled; + + /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this + * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ + private int mTransactionSequence; + + /** Only do a maximum of 6 repeated layouts. After that quit */ + private int mLayoutRepeatCount; + + final WindowAnimator mAnimator; + + SparseArray<Task> mTaskIdToTask = new SparseArray<Task>(); + SparseArray<TaskStack> mStackIdToStack = new SparseArray<TaskStack>(); + + private final PointerEventDispatcher mPointerEventDispatcher; + + final class DragInputEventReceiver extends InputEventReceiver { + public DragInputEventReceiver(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + try { + if (event instanceof MotionEvent + && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 + && mDragState != null) { + final MotionEvent motionEvent = (MotionEvent)event; + boolean endDrag = false; + final float newX = motionEvent.getRawX(); + final float newY = motionEvent.getRawY(); + + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: { + if (DEBUG_DRAG) { + Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer"); + } + } break; + + case MotionEvent.ACTION_MOVE: { + synchronized (mWindowMap) { + // move the surface and tell the involved window(s) where we are + mDragState.notifyMoveLw(newX, newY); + } + } break; + + case MotionEvent.ACTION_UP: { + if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at " + + newX + "," + newY); + synchronized (mWindowMap) { + endDrag = mDragState.notifyDropLw(newX, newY); + } + } break; + + case MotionEvent.ACTION_CANCEL: { + if (DEBUG_DRAG) Slog.d(TAG, "Drag cancelled!"); + endDrag = true; + } break; + } + + if (endDrag) { + if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state"); + // tell all the windows that the drag has ended + synchronized (mWindowMap) { + mDragState.endDragLw(); + } + } + + handled = true; + } + } catch (Exception e) { + Slog.e(TAG, "Exception caught by drag handleMotion", e); + } finally { + finishInputEvent(event, handled); + } + } + } + + /** + * Whether the UI is currently running in touch mode (not showing + * navigational focus because the user is directly pressing the screen). + */ + boolean mInTouchMode = true; + + private ViewServer mViewServer; + private final ArrayList<WindowChangeListener> mWindowChangeListeners = + new ArrayList<WindowChangeListener>(); + private boolean mWindowsChanged = false; + + public interface WindowChangeListener { + public void windowsChanged(); + public void focusChanged(); + } + + final Configuration mTempConfiguration = new Configuration(); + + // The desired scaling factor for compatible apps. + float mCompatibleScreenScale; + + // If true, only the core apps and services are being launched because the device + // is in a special boot mode, such as being encrypted or waiting for a decryption password. + // For example, when this flag is true, there will be no wallpaper service. + final boolean mOnlyCore; + + public static WindowManagerService main(final Context context, + final PowerManagerService pm, final DisplayManagerService dm, + final InputManagerService im, final Handler wmHandler, + final boolean haveInputMethods, final boolean showBootMsgs, + final boolean onlyCore) { + final WindowManagerService[] holder = new WindowManagerService[1]; + wmHandler.runWithScissors(new Runnable() { + @Override + public void run() { + holder[0] = new WindowManagerService(context, pm, dm, im, + haveInputMethods, showBootMsgs, onlyCore); + } + }, 0); + return holder[0]; + } + + private void initPolicy(Handler uiHandler) { + uiHandler.runWithScissors(new Runnable() { + @Override + public void run() { + WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); + + mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); + mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() + * TYPE_LAYER_MULTIPLIER + + TYPE_LAYER_OFFSET; + } + }, 0); + } + + private WindowManagerService(Context context, PowerManagerService pm, + DisplayManagerService displayManager, InputManagerService inputManager, + boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { + mContext = context; + mHaveInputMethods = haveInputMethods; + mAllowBootMessages = showBootMsgs; + mOnlyCore = onlyCore; + mLimitedAlphaCompositing = context.getResources().getBoolean( + com.android.internal.R.bool.config_sf_limitedAlpha); + mInputManager = inputManager; // Must be before createDisplayContentLocked. + mDisplayManagerService = displayManager; + mDisplaySettings = new DisplaySettings(context); + mDisplaySettings.readSettingsLocked(); + + mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG)); + + mFxSession = new SurfaceSession(); + mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); + mDisplayManager.registerDisplayListener(this, null); + Display[] displays = mDisplayManager.getDisplays(); + for (Display display : displays) { + createDisplayContentLocked(display); + } + + mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); + + mPowerManager = pm; + mPowerManager.setPolicy(mPolicy); + PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mScreenFrozenLock = pmc.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); + mScreenFrozenLock.setReferenceCounted(false); + + mAppTransition = new AppTransition(context, mH); + + mActivityManager = ActivityManagerNative.getDefault(); + mBatteryStats = BatteryStatsService.getService(); + mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); + mAppOps.startWatchingMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, null, + new AppOpsManager.OnOpChangedInternalListener() { + @Override + public void onOpChanged(int op, String packageName) { + updateAppOpsState(); + } + } + ); + + // Get persisted window scale setting + mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(), + Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); + mTransitionAnimationScale = Settings.Global.getFloat(context.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); + setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale)); + + // Track changes to DevicePolicyManager state so we can enable/disable keyguard. + IntentFilter filter = new IntentFilter(); + filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, filter); + + mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK + | PowerManager.ON_AFTER_RELEASE, TAG); + mHoldingScreenWakeLock.setReferenceCounted(false); + + mAnimator = new WindowAnimator(this); + + initPolicy(UiThread.getHandler()); + + // Add ourself to the Watchdog monitors. + Watchdog.getInstance().addMonitor(this); + + SurfaceControl.openTransaction(); + try { + createWatermarkInTransaction(); + mFocusedStackFrame = new FocusedStackFrame( + getDefaultDisplayContentLocked().getDisplay(), mFxSession); + } finally { + SurfaceControl.closeTransaction(); + } + } + + public InputMonitor getInputMonitor() { + return mInputMonitor; + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + // The window manager only throws security exceptions, so let's + // log all others. + if (!(e instanceof SecurityException)) { + Slog.wtf(TAG, "Window Manager Crash", e); + } + throw e; + } + } + + private void placeWindowAfter(WindowState pos, WindowState window) { + final WindowList windows = pos.getWindowList(); + final int i = windows.indexOf(pos); + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( + TAG, "Adding window " + window + " at " + + (i+1) + " of " + windows.size() + " (after " + pos + ")"); + windows.add(i+1, window); + mWindowsChanged = true; + } + + private void placeWindowBefore(WindowState pos, WindowState window) { + final WindowList windows = pos.getWindowList(); + int i = windows.indexOf(pos); + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( + TAG, "Adding window " + window + " at " + + i + " of " + windows.size() + " (before " + pos + ")"); + if (i < 0) { + Slog.w(TAG, "placeWindowBefore: Unable to find " + pos + " in " + windows); + i = 0; + } + windows.add(i, window); + mWindowsChanged = true; + } + + //This method finds out the index of a window that has the same app token as + //win. used for z ordering the windows in mWindows + private int findIdxBasedOnAppTokens(WindowState win) { + WindowList windows = win.getWindowList(); + for(int j = windows.size() - 1; j >= 0; j--) { + WindowState wentry = windows.get(j); + if(wentry.mAppToken == win.mAppToken) { + return j; + } + } + return -1; + } + + /** + * Return the list of Windows from the passed token on the given Display. + * @param token The token with all the windows. + * @param displayContent The display we are interested in. + * @return List of windows from token that are on displayContent. + */ + WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) { + final WindowList windowList = new WindowList(); + final int count = token.windows.size(); + for (int i = 0; i < count; i++) { + final WindowState win = token.windows.get(i); + if (win.mDisplayContent == displayContent) { + windowList.add(win); + } + } + return windowList; + } + + /** + * Recursive search through a WindowList and all of its windows' children. + * @param targetWin The window to search for. + * @param windows The list to search. + * @return The index of win in windows or of the window that is an ancestor of win. + */ + private int indexOfWinInWindowList(WindowState targetWin, WindowList windows) { + for (int i = windows.size() - 1; i >= 0; i--) { + final WindowState w = windows.get(i); + if (w == targetWin) { + return i; + } + if (!w.mChildWindows.isEmpty()) { + if (indexOfWinInWindowList(targetWin, w.mChildWindows) >= 0) { + return i; + } + } + } + return -1; + } + + private int addAppWindowToListLocked(final WindowState win) { + final IWindow client = win.mClient; + final WindowToken token = win.mToken; + final DisplayContent displayContent = win.mDisplayContent; + + final WindowList windows = win.getWindowList(); + final int N = windows.size(); + WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); + int tokenWindowsPos = 0; + int windowListPos = tokenWindowList.size(); + if (!tokenWindowList.isEmpty()) { + // If this application has existing windows, we + // simply place the new window on top of them... but + // keep the starting window on top. + if (win.mAttrs.type == TYPE_BASE_APPLICATION) { + // Base windows go behind everything else. + WindowState lowestWindow = tokenWindowList.get(0); + placeWindowBefore(lowestWindow, win); + tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows); + } else { + AppWindowToken atoken = win.mAppToken; + WindowState lastWindow = tokenWindowList.get(windowListPos - 1); + if (atoken != null && lastWindow == atoken.startingWindow) { + placeWindowBefore(lastWindow, win); + tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows); + } else { + int newIdx = findIdxBasedOnAppTokens(win); + //there is a window above this one associated with the same + //apptoken note that the window could be a floating window + //that was created later or a window at the top of the list of + //windows associated with this token. + if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " + + N); + windows.add(newIdx + 1, win); + if (newIdx < 0) { + // No window from token found on win's display. + tokenWindowsPos = 0; + } else { + tokenWindowsPos = indexOfWinInWindowList( + windows.get(newIdx), token.windows) + 1; + } + mWindowsChanged = true; + } + } + return tokenWindowsPos; + } + + // No windows from this token on this display + if (localLOGV) Slog.v(TAG, "Figuring out where to add app window " + client.asBinder() + + " (token=" + token + ")"); + // Figure out where the window should go, based on the + // order of applications. + WindowState pos = null; + + final ArrayList<Task> tasks = displayContent.getTasks(); + int taskNdx; + int tokenNdx = -1; + for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken t = tokens.get(tokenNdx); + if (t == token) { + --tokenNdx; + if (tokenNdx < 0) { + --taskNdx; + if (taskNdx >= 0) { + tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1; + } + } + break; + } + + // We haven't reached the token yet; if this token + // is not going to the bottom and has windows on this display, we can + // use it as an anchor for when we do reach the token. + tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); + if (!t.sendingToBottom && tokenWindowList.size() > 0) { + pos = tokenWindowList.get(0); + } + } + if (tokenNdx >= 0) { + // early exit + break; + } + } + + // We now know the index into the apps. If we found + // an app window above, that gives us the position; else + // we need to look some more. + if (pos != null) { + // Move behind any windows attached to this one. + WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); + if (atoken != null) { + tokenWindowList = + getTokenWindowsOnDisplay(atoken, displayContent); + final int NC = tokenWindowList.size(); + if (NC > 0) { + WindowState bottom = tokenWindowList.get(0); + if (bottom.mSubLayer < 0) { + pos = bottom; + } + } + } + placeWindowBefore(pos, win); + return tokenWindowsPos; + } + + // Continue looking down until we find the first + // token that has windows on this display. + for ( ; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for ( ; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken t = tokens.get(tokenNdx); + tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); + final int NW = tokenWindowList.size(); + if (NW > 0) { + pos = tokenWindowList.get(NW-1); + break; + } + } + if (tokenNdx >= 0) { + // found + break; + } + } + + if (pos != null) { + // Move in front of any windows attached to this + // one. + WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); + if (atoken != null) { + final int NC = atoken.windows.size(); + if (NC > 0) { + WindowState top = atoken.windows.get(NC-1); + if (top.mSubLayer >= 0) { + pos = top; + } + } + } + placeWindowAfter(pos, win); + return tokenWindowsPos; + } + + // Just search for the start of this layer. + final int myLayer = win.mBaseLayer; + int i; + for (i = 0; i < N; i++) { + WindowState w = windows.get(i); + if (w.mBaseLayer > myLayer) { + break; + } + } + if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Based on layer: Adding window " + win + " at " + i + " of " + N); + windows.add(i, win); + mWindowsChanged = true; + return tokenWindowsPos; + } + + private void addFreeWindowToListLocked(final WindowState win) { + final WindowList windows = win.getWindowList(); + + // Figure out where window should go, based on layer. + final int myLayer = win.mBaseLayer; + int i; + for (i = windows.size() - 1; i >= 0; i--) { + if (windows.get(i).mBaseLayer <= myLayer) { + break; + } + } + i++; + if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Free window: Adding window " + win + " at " + i + " of " + windows.size()); + windows.add(i, win); + mWindowsChanged = true; + } + + private void addAttachedWindowToListLocked(final WindowState win, boolean addToToken) { + final WindowToken token = win.mToken; + final DisplayContent displayContent = win.mDisplayContent; + final WindowState attached = win.mAttachedWindow; + + WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); + + // Figure out this window's ordering relative to the window + // it is attached to. + final int NA = tokenWindowList.size(); + final int sublayer = win.mSubLayer; + int largestSublayer = Integer.MIN_VALUE; + WindowState windowWithLargestSublayer = null; + int i; + for (i = 0; i < NA; i++) { + WindowState w = tokenWindowList.get(i); + final int wSublayer = w.mSubLayer; + if (wSublayer >= largestSublayer) { + largestSublayer = wSublayer; + windowWithLargestSublayer = w; + } + if (sublayer < 0) { + // For negative sublayers, we go below all windows + // in the same sublayer. + if (wSublayer >= sublayer) { + if (addToToken) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "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); + } + } + } + + private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) { + if (DEBUG_FOCUS_LIGHT) Slog.d(TAG, "addWindowToListInOrderLocked: win=" + win + + " Callers=" + Debug.getCallers(4)); + if (win.mAttachedWindow == null) { + final WindowToken token = win.mToken; + int tokenWindowsPos = 0; + if (token.appWindowToken != null) { + tokenWindowsPos = addAppWindowToListLocked(win); + } else { + addFreeWindowToListLocked(win); + } + if (addToToken) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); + token.windows.add(tokenWindowsPos, win); + } + } else { + addAttachedWindowToListLocked(win, addToToken); + } + + 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 == TYPE_APPLICATION_STARTING) { + if (DEBUG_INPUT_METHOD) { + Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding()); + if (!w.isVisibleOrAdding()) { + Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurfaceControl + + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility + + " policyVis=" + w.mPolicyVisibility + + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim + + " attachHid=" + w.mAttachedHidden + + " exiting=" + w.mExiting + " destroying=" + w.mDestroying); + if (w.mAppToken != null) { + Slog.i(TAG, " mAppToken.hiddenRequested=" + w.mAppToken.hiddenRequested); + } + } + } + return w.isVisibleOrAdding(); + } + return false; + } + + /** + * Dig through the WindowStates and find the one that the Input Method will target. + * @param willMove + * @return The index+1 in mWindows of the discovered target. + */ + int findDesiredInputMethodWindowIndexLocked(boolean willMove) { + // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the + // same display. Or even when the current IME/target are not on the same screen as the next + // IME/target. For now only look for input windows on the main screen. + WindowList windows = getDefaultWindowListLocked(); + WindowState w = null; + int i; + for (i = windows.size() - 1; i >= 0; --i) { + WindowState win = windows.get(i); + + if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG, "Checking window @" + i + + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags)); + if (canBeImeTarget(win)) { + w = win; + //Slog.i(TAG, "Putting input method here!"); + + // Yet more tricksyness! If this window is a "starting" + // window, we do actually want to be on top of it, but + // it is not -really- where input will go. So if the caller + // is not actually looking to move the IME, look down below + // for a real window to target... + if (!willMove + && w.mAttrs.type == TYPE_APPLICATION_STARTING + && i > 0) { + WindowState wb = windows.get(i-1); + if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { + i--; + w = wb; + } + } + break; + } + } + + // Now w is either mWindows[0] or an IME (or null if mWindows is empty). + + if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG, "Proposed new IME target: " + w); + + // Now, a special case -- if the last target's window is in the + // process of exiting, and is above the new target, keep on the + // last target to avoid flicker. Consider for example a Dialog with + // the IME shown: when the Dialog is dismissed, we want to keep + // the IME above it until it is completely gone so it doesn't drop + // behind the dialog or its full-screen scrim. + final WindowState curTarget = mInputMethodTarget; + if (curTarget != null + && curTarget.isDisplayedLw() + && curTarget.isClosing() + && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) { + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, not changing"); + return windows.indexOf(curTarget) + 1; + } + + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Desired input method target=" + + w + " willMove=" + willMove); + + if (willMove && w != null) { + AppWindowToken token = curTarget == null ? null : curTarget.mAppToken; + if (token != null) { + + // Now some fun for dealing with window animations that + // modify the Z order. We need to look at all windows below + // the current target that are in this app, finding the highest + // visible one in layering. + WindowState highestTarget = null; + int highestPos = 0; + if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) { + WindowList curWindows = curTarget.getWindowList(); + int pos = curWindows.indexOf(curTarget); + while (pos >= 0) { + WindowState win = curWindows.get(pos); + if (win.mAppToken != token) { + break; + } + if (!win.mRemoved) { + if (highestTarget == null || win.mWinAnimator.mAnimLayer > + highestTarget.mWinAnimator.mAnimLayer) { + highestTarget = win; + highestPos = pos; + } + } + pos--; + } + } + + if (highestTarget != null) { + if (DEBUG_INPUT_METHOD) Slog.v(TAG, mAppTransition + " " + highestTarget + + " animating=" + highestTarget.mWinAnimator.isAnimating() + + " layer=" + highestTarget.mWinAnimator.mAnimLayer + + " new layer=" + w.mWinAnimator.mAnimLayer); + + if (mAppTransition.isTransitionSet()) { + // If we are currently setting up for an animation, + // hold everything until we can find out what will happen. + mInputMethodTargetWaitingAnim = true; + mInputMethodTarget = highestTarget; + return highestPos + 1; + } else if (highestTarget.mWinAnimator.isAnimating() && + highestTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) { + // If the window we are currently targeting is involved + // with an animation, and it is on top of the next target + // we will be over, then hold off on moving until + // that is done. + mInputMethodTargetWaitingAnim = true; + mInputMethodTarget = highestTarget; + return highestPos + 1; + } + } + } + } + + //Slog.i(TAG, "Placing input method @" + (i+1)); + if (w != null) { + if (willMove) { + if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to " + + w + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4))); + mInputMethodTarget = w; + mInputMethodTargetWaitingAnim = false; + if (w.mAppToken != null) { + setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment); + } else { + setInputMethodAnimLayerAdjustment(0); + } + } + return i+1; + } + if (willMove) { + if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to null." + + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4))); + mInputMethodTarget = null; + setInputMethodAnimLayerAdjustment(0); + } + return -1; + } + + void addInputMethodWindowToListLocked(WindowState win) { + int pos = findDesiredInputMethodWindowIndexLocked(true); + if (pos >= 0) { + win.mTargetAppToken = mInputMethodTarget.mAppToken; + if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( + TAG, "Adding input method window " + win + " at " + pos); + // TODO(multidisplay): IMEs are only supported on the default display. + getDefaultWindowListLocked().add(pos, win); + mWindowsChanged = true; + moveInputMethodDialogsLocked(pos+1); + return; + } + win.mTargetAppToken = null; + addWindowToListInOrderLocked(win, true); + moveInputMethodDialogsLocked(pos); + } + + void setInputMethodAnimLayerAdjustment(int adj) { + if (DEBUG_LAYERS) Slog.v(TAG, "Setting im layer adj to " + adj); + mInputMethodAnimLayerAdjustment = adj; + WindowState imw = mInputMethodWindow; + if (imw != null) { + imw.mWinAnimator.mAnimLayer = imw.mLayer + adj; + if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + + " anim layer: " + imw.mWinAnimator.mAnimLayer); + int wi = imw.mChildWindows.size(); + while (wi > 0) { + wi--; + WindowState cw = imw.mChildWindows.get(wi); + cw.mWinAnimator.mAnimLayer = cw.mLayer + adj; + if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw + + " anim layer: " + cw.mWinAnimator.mAnimLayer); + } + } + int di = mInputMethodDialogs.size(); + while (di > 0) { + di --; + imw = mInputMethodDialogs.get(di); + imw.mWinAnimator.mAnimLayer = imw.mLayer + adj; + if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw + + " anim layer: " + imw.mWinAnimator.mAnimLayer); + } + } + + private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { + WindowList windows = win.getWindowList(); + int wpos = windows.indexOf(win); + if (wpos >= 0) { + if (wpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win); + windows.remove(wpos); + mWindowsChanged = true; + int NC = win.mChildWindows.size(); + while (NC > 0) { + NC--; + WindowState cw = win.mChildWindows.get(NC); + int cpos = windows.indexOf(cw); + if (cpos >= 0) { + if (cpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing child at " + + cpos + ": " + cw); + windows.remove(cpos); + } + } + } + return interestingPos; + } + + private void reAddWindowToListInOrderLocked(WindowState win) { + addWindowToListInOrderLocked(win, false); + // This is a hack to get all of the child windows added as well + // at the right position. Child windows should be rare and + // this case should be rare, so it shouldn't be that big a deal. + WindowList windows = win.getWindowList(); + int wpos = windows.indexOf(win); + if (wpos >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos + ": " + win); + windows.remove(wpos); + mWindowsChanged = true; + reAddWindowLocked(wpos, win); + } + } + + void logWindowList(final WindowList windows, String prefix) { + int N = windows.size(); + while (N > 0) { + N--; + Slog.v(TAG, prefix + "#" + N + ": " + windows.get(N)); + } + } + + void moveInputMethodDialogsLocked(int pos) { + ArrayList<WindowState> dialogs = mInputMethodDialogs; + + // TODO(multidisplay): IMEs are only supported on the default display. + WindowList windows = getDefaultWindowListLocked(); + final int N = dialogs.size(); + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); + for (int i=0; i<N; i++) { + pos = tmpRemoveWindowLocked(pos, dialogs.get(i)); + } + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "Window list w/pos=" + pos); + logWindowList(windows, " "); + } + + if (pos >= 0) { + final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; + if (pos < windows.size()) { + WindowState wp = windows.get(pos); + if (wp == mInputMethodWindow) { + pos++; + } + } + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Adding " + N + " dialogs at pos=" + pos); + for (int i=0; i<N; i++) { + WindowState win = dialogs.get(i); + win.mTargetAppToken = targetAppToken; + pos = reAddWindowLocked(pos, win); + } + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "Final window list:"); + logWindowList(windows, " "); + } + return; + } + for (int i=0; i<N; i++) { + WindowState win = dialogs.get(i); + win.mTargetAppToken = null; + reAddWindowToListInOrderLocked(win); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "No IM target, final list:"); + logWindowList(windows, " "); + } + } + } + + boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) { + final WindowState imWin = mInputMethodWindow; + final int DN = mInputMethodDialogs.size(); + if (imWin == null && DN == 0) { + return false; + } + + // TODO(multidisplay): IMEs are only supported on the default display. + WindowList windows = getDefaultWindowListLocked(); + + int imPos = findDesiredInputMethodWindowIndexLocked(true); + if (imPos >= 0) { + // In this case, the input method windows are to be placed + // immediately above the window they are targeting. + + // First check to see if the input method windows are already + // located here, and contiguous. + final int N = windows.size(); + WindowState firstImWin = imPos < N + ? windows.get(imPos) : null; + + // Figure out the actual input method window that should be + // at the bottom of their stack. + WindowState baseImWin = imWin != null + ? imWin : mInputMethodDialogs.get(0); + if (baseImWin.mChildWindows.size() > 0) { + WindowState cw = baseImWin.mChildWindows.get(0); + if (cw.mSubLayer < 0) baseImWin = cw; + } + + if (firstImWin == baseImWin) { + // The windows haven't moved... but are they still contiguous? + // First find the top IM window. + int pos = imPos+1; + while (pos < N) { + if (!(windows.get(pos)).mIsImWindow) { + break; + } + pos++; + } + pos++; + // Now there should be no more input method windows above. + while (pos < N) { + if ((windows.get(pos)).mIsImWindow) { + break; + } + pos++; + } + if (pos >= N) { + // Z order is good. + // The IM target window may be changed, so update the mTargetAppToken. + if (imWin != null) { + imWin.mTargetAppToken = mInputMethodTarget.mAppToken; + } + return false; + } + } + + if (imWin != null) { + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "Moving IM from " + imPos); + logWindowList(windows, " "); + } + imPos = tmpRemoveWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "List after removing with new pos " + imPos + ":"); + logWindowList(windows, " "); + } + imWin.mTargetAppToken = mInputMethodTarget.mAppToken; + reAddWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "List after moving IM to " + imPos + ":"); + logWindowList(windows, " "); + } + if (DN > 0) moveInputMethodDialogsLocked(imPos+1); + } else { + moveInputMethodDialogsLocked(imPos); + } + + } else { + // In this case, the input method windows go in a fixed layer, + // because they aren't currently associated with a focus window. + + if (imWin != null) { + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Moving IM from " + imPos); + tmpRemoveWindowLocked(0, imWin); + imWin.mTargetAppToken = null; + reAddWindowToListInOrderLocked(imWin); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG, "List with no IM target:"); + logWindowList(windows, " "); + } + if (DN > 0) moveInputMethodDialogsLocked(-1); + } else { + moveInputMethodDialogsLocked(-1); + } + + } + + if (needAssignLayers) { + assignLayersLocked(windows); + } + + return true; + } + + final boolean isWallpaperVisible(WindowState wallpaperTarget) { + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured=" + + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") + + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null) + ? wallpaperTarget.mAppToken.mAppAnimator.animation : null) + + " upper=" + mUpperWallpaperTarget + + " lower=" + mLowerWallpaperTarget); + return (wallpaperTarget != null + && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null + && wallpaperTarget.mAppToken.mAppAnimator.animation != null))) + || mUpperWallpaperTarget != null + || mLowerWallpaperTarget != null; + } + + static final int ADJUST_WALLPAPER_LAYERS_CHANGED = 1<<1; + static final int ADJUST_WALLPAPER_VISIBILITY_CHANGED = 1<<2; + + int adjustWallpaperWindowsLocked() { + mInnerFields.mWallpaperMayChange = false; + boolean targetChanged = false; + + // TODO(multidisplay): Wallpapers on main screen only. + final DisplayInfo displayInfo = getDefaultDisplayContentLocked().getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; + + // First find top-most window that has asked to be on top of the + // wallpaper; all wallpapers go behind it. + final WindowList windows = getDefaultWindowListLocked(); + int N = windows.size(); + WindowState w = null; + WindowState foundW = null; + int foundI = 0; + WindowState topCurW = null; + int topCurI = 0; + int windowDetachedI = -1; + int i = N; + while (i > 0) { + i--; + w = windows.get(i); + if ((w.mAttrs.type == TYPE_WALLPAPER)) { + if (topCurW == null) { + topCurW = w; + topCurI = i; + } + continue; + } + topCurW = null; + if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) { + // If this window's app token is hidden and not animating, + // it is of no interest to us. + if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) { + if (DEBUG_WALLPAPER) Slog.v(TAG, + "Skipping hidden and not animating token: " + w); + continue; + } + } + if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen=" + + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState); + if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isOnScreen() + && (mWallpaperTarget == w || w.isDrawFinishedLw())) { + if (DEBUG_WALLPAPER) Slog.v(TAG, + "Found wallpaper target: #" + i + "=" + w); + foundW = w; + foundI = i; + if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) { + // The current wallpaper target is animating, so we'll + // look behind it for another possible target and figure + // out what is going on below. + if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + + ": token animating, looking behind."); + continue; + } + break; + } else if (w == mAnimator.mWindowDetachedWallpaper) { + windowDetachedI = i; + } + } + + if (foundW == null && windowDetachedI >= 0) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Found animating detached wallpaper activity: #" + i + "=" + w); + foundW = w; + foundI = windowDetachedI; + } + + if (mWallpaperTarget != foundW + && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) { + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "New wallpaper target: " + foundW + + " oldTarget: " + mWallpaperTarget); + } + + mLowerWallpaperTarget = null; + mUpperWallpaperTarget = null; + + WindowState oldW = mWallpaperTarget; + mWallpaperTarget = foundW; + targetChanged = true; + + // Now what is happening... if the current and new targets are + // animating, then we are in our super special mode! + if (foundW != null && oldW != null) { + boolean oldAnim = oldW.isAnimatingLw(); + boolean foundAnim = foundW.isAnimatingLw(); + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "New animation: " + foundAnim + + " old animation: " + oldAnim); + } + if (foundAnim && oldAnim) { + int oldI = windows.indexOf(oldW); + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); + } + if (oldI >= 0) { + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "Animating wallpapers: old#" + oldI + + "=" + oldW + "; new#" + foundI + + "=" + foundW); + } + + // Set the new target correctly. + if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) { + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "Old wallpaper still the target."); + } + mWallpaperTarget = oldW; + foundW = oldW; + foundI = oldI; + } + // Now set the upper and lower wallpaper targets + // correctly, and make sure that we are positioning + // the wallpaper below the lower. + else if (foundI > oldI) { + // The new target is on top of the old one. + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "Found target above old target."); + } + mUpperWallpaperTarget = foundW; + mLowerWallpaperTarget = oldW; + foundW = oldW; + foundI = oldI; + } else { + // The new target is below the old one. + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "Found target below old target."); + } + mUpperWallpaperTarget = oldW; + mLowerWallpaperTarget = foundW; + } + } + } + } + + } else if (mLowerWallpaperTarget != null) { + // Is it time to stop animating? + if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) { + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG, "No longer animating wallpaper targets!"); + } + mLowerWallpaperTarget = null; + mUpperWallpaperTarget = null; + mWallpaperTarget = foundW; + targetChanged = true; + } + } + + boolean visible = foundW != null; + if (visible) { + // The window is visible to the compositor... but is it visible + // to the user? That is what the wallpaper cares about. + visible = isWallpaperVisible(foundW); + if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible); + + // If the wallpaper target is animating, we may need to copy + // its layer adjustment. Only do this if we are not transfering + // between two wallpaper targets. + mWallpaperAnimLayerAdjustment = + (mLowerWallpaperTarget == null && foundW.mAppToken != null) + ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0; + + final int maxLayer = mPolicy.getMaxWallpaperLayer() + * TYPE_LAYER_MULTIPLIER + + TYPE_LAYER_OFFSET; + + // Now w is the window we are supposed to be behind... but we + // need to be sure to also be behind any of its attached windows, + // AND any starting window associated with it, AND below the + // maximum layer the policy allows for wallpapers. + while (foundI > 0) { + WindowState wb = windows.get(foundI-1); + if (wb.mBaseLayer < maxLayer && + wb.mAttachedWindow != foundW && + (foundW.mAttachedWindow == null || + wb.mAttachedWindow != foundW.mAttachedWindow) && + (wb.mAttrs.type != TYPE_APPLICATION_STARTING || + foundW.mToken == null || wb.mToken != foundW.mToken)) { + // This window is not related to the previous one in any + // interesting way, so stop here. + break; + } + foundW = wb; + foundI--; + } + } else { + if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target"); + } + + if (foundW == null && topCurW != null) { + // There is no wallpaper target, so it goes at the bottom. + // We will assume it is the same place as last time, if known. + foundW = topCurW; + foundI = topCurI+1; + } else { + // Okay i is the position immediately above the wallpaper. Look at + // what is below it for later. + foundW = foundI > 0 ? windows.get(foundI-1) : null; + } + + if (visible) { + if (mWallpaperTarget.mWallpaperX >= 0) { + mLastWallpaperX = mWallpaperTarget.mWallpaperX; + mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; + } + if (mWallpaperTarget.mWallpaperY >= 0) { + mLastWallpaperY = mWallpaperTarget.mWallpaperY; + mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; + } + } + + // Start stepping backwards from here, ensuring that our wallpaper windows + // are correctly placed. + int changed = 0; + int curTokenIndex = mWallpaperTokens.size(); + while (curTokenIndex > 0) { + curTokenIndex--; + WindowToken token = mWallpaperTokens.get(curTokenIndex); + if (token.hidden == visible) { + if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, + "Wallpaper token " + token + " hidden=" + !visible); + changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED; + token.hidden = !visible; + // Need to do a layout to ensure the wallpaper now has the + // correct size. + getDefaultDisplayContentLocked().layoutNeeded = true; + } + + int curWallpaperIndex = token.windows.size(); + while (curWallpaperIndex > 0) { + curWallpaperIndex--; + WindowState wallpaper = token.windows.get(curWallpaperIndex); + + if (visible) { + updateWallpaperOffsetLocked(wallpaper, dw, dh, false); + } + + // First, make sure the client has the current visibility + // state. + dispatchWallpaperVisibility(wallpaper, visible); + + wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment; + if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win " + + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); + + // First, if this window is at the current index, then all + // is well. + if (wallpaper == foundW) { + foundI--; + foundW = foundI > 0 + ? windows.get(foundI-1) : null; + continue; + } + + // The window didn't match... the current wallpaper window, + // wherever it is, is in the wrong place, so make sure it is + // not in the list. + int oldIndex = windows.indexOf(wallpaper); + if (oldIndex >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " + + oldIndex + ": " + wallpaper); + windows.remove(oldIndex); + mWindowsChanged = true; + if (oldIndex < foundI) { + foundI--; + } + } + + // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost + // layer. For keyguard over wallpaper put the wallpaper under the keyguard. + int insertionIndex = 0; + if (visible && foundW != null) { + final int type = foundW.mAttrs.type; + if (type == TYPE_KEYGUARD || type == TYPE_KEYGUARD_SCRIM) { + insertionIndex = windows.indexOf(foundW); + } + } + if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { + Slog.v(TAG, "Moving wallpaper " + wallpaper + + " from " + oldIndex + " to " + insertionIndex); + } + + windows.add(insertionIndex, wallpaper); + mWindowsChanged = true; + changed |= ADJUST_WALLPAPER_LAYERS_CHANGED; + } + } + + /* + final TaskStack targetStack = + mWallpaperTarget == null ? null : mWallpaperTarget.getStack(); + if ((changed & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0 && + targetStack != null && !targetStack.isHomeStack()) { + // If the wallpaper target is not on the home stack then make sure that all windows + // from other non-home stacks are above the wallpaper. + for (i = foundI - 1; i >= 0; --i) { + WindowState win = windows.get(i); + if (!win.isVisibleLw()) { + continue; + } + final TaskStack winStack = win.getStack(); + if (winStack != null && !winStack.isHomeStack() && winStack != targetStack) { + windows.remove(i); + windows.add(foundI + 1, win); + } + } + } + */ + + if (targetChanged && DEBUG_WALLPAPER_LIGHT) { + Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget + + " lower=" + mLowerWallpaperTarget + " upper=" + + mUpperWallpaperTarget); + } + + return changed; + } + + void setWallpaperAnimLayerAdjustmentLocked(int adj) { + if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, + "Setting wallpaper layer adj to " + adj); + mWallpaperAnimLayerAdjustment = adj; + int curTokenIndex = mWallpaperTokens.size(); + while (curTokenIndex > 0) { + curTokenIndex--; + WindowToken token = mWallpaperTokens.get(curTokenIndex); + int curWallpaperIndex = token.windows.size(); + while (curWallpaperIndex > 0) { + curWallpaperIndex--; + WindowState wallpaper = token.windows.get(curWallpaperIndex); + wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + adj; + if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "setWallpaper win " + + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); + } + } + } + + boolean updateWallpaperOffsetLocked(WindowState wallpaperWin, int dw, int dh, + boolean sync) { + boolean changed = false; + boolean rawChanged = false; + float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f; + float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; + int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw; + int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0; + changed = wallpaperWin.mXOffset != offset; + if (changed) { + if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + + wallpaperWin + " x: " + offset); + wallpaperWin.mXOffset = offset; + } + if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) { + wallpaperWin.mWallpaperX = wpx; + wallpaperWin.mWallpaperXStep = wpxs; + rawChanged = true; + } + + float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; + float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; + int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh; + offset = availh > 0 ? -(int)(availh*wpy+.5f) : 0; + if (wallpaperWin.mYOffset != offset) { + if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + + wallpaperWin + " y: " + offset); + changed = true; + wallpaperWin.mYOffset = offset; + } + if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) { + wallpaperWin.mWallpaperY = wpy; + wallpaperWin.mWallpaperYStep = wpys; + rawChanged = true; + } + + if (rawChanged && (wallpaperWin.mAttrs.privateFlags & + WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { + try { + if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " + + wallpaperWin + " x=" + wallpaperWin.mWallpaperX + + " y=" + wallpaperWin.mWallpaperY); + if (sync) { + mWaitingOnWallpaper = wallpaperWin; + } + wallpaperWin.mClient.dispatchWallpaperOffsets( + wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY, + wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync); + if (sync) { + if (mWaitingOnWallpaper != null) { + long start = SystemClock.uptimeMillis(); + if ((mLastWallpaperTimeoutTime+WALLPAPER_TIMEOUT_RECOVERY) + < start) { + try { + if (DEBUG_WALLPAPER) Slog.v(TAG, + "Waiting for offset complete..."); + mWindowMap.wait(WALLPAPER_TIMEOUT); + } catch (InterruptedException e) { + } + if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!"); + if ((start+WALLPAPER_TIMEOUT) + < SystemClock.uptimeMillis()) { + Slog.i(TAG, "Timeout waiting for wallpaper to offset: " + + wallpaperWin); + mLastWallpaperTimeoutTime = start; + } + } + mWaitingOnWallpaper = null; + } + } + } catch (RemoteException e) { + } + } + + return changed; + } + + void wallpaperOffsetsComplete(IBinder window) { + synchronized (mWindowMap) { + if (mWaitingOnWallpaper != null && + mWaitingOnWallpaper.mClient.asBinder() == window) { + mWaitingOnWallpaper = null; + mWindowMap.notifyAll(); + } + } + } + + void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { + final DisplayContent displayContent = changingTarget.mDisplayContent; + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; + + WindowState target = mWallpaperTarget; + if (target != null) { + if (target.mWallpaperX >= 0) { + mLastWallpaperX = target.mWallpaperX; + } else if (changingTarget.mWallpaperX >= 0) { + mLastWallpaperX = changingTarget.mWallpaperX; + } + if (target.mWallpaperY >= 0) { + mLastWallpaperY = target.mWallpaperY; + } else if (changingTarget.mWallpaperY >= 0) { + mLastWallpaperY = changingTarget.mWallpaperY; + } + } + + int curTokenIndex = mWallpaperTokens.size(); + while (curTokenIndex > 0) { + curTokenIndex--; + WindowToken token = mWallpaperTokens.get(curTokenIndex); + int curWallpaperIndex = token.windows.size(); + while (curWallpaperIndex > 0) { + curWallpaperIndex--; + WindowState wallpaper = token.windows.get(curWallpaperIndex); + if (updateWallpaperOffsetLocked(wallpaper, dw, dh, sync)) { + WindowStateAnimator winAnimator = wallpaper.mWinAnimator; + winAnimator.computeShownFrameLocked(); + // No need to lay out the windows - we can just set the wallpaper position + // directly. + winAnimator.setWallpaperOffset(wallpaper.mShownFrame); + // We only want to be synchronous with one wallpaper. + sync = false; + } + } + } + } + + /** + * Check wallpaper for visiblity change and notify window if so. + * @param wallpaper The wallpaper to test and notify. + * @param visible Current visibility. + */ + void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) { + if (wallpaper.mWallpaperVisible != visible) { + wallpaper.mWallpaperVisible = visible; + try { + if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Updating vis of wallpaper " + wallpaper + + ": " + visible + " from:\n" + Debug.getCallers(4, " ")); + wallpaper.mClient.dispatchAppVisibility(visible); + } catch (RemoteException e) { + } + } + } + + void updateWallpaperVisibilityLocked() { + final boolean visible = isWallpaperVisible(mWallpaperTarget); + final DisplayContent displayContent = mWallpaperTarget.mDisplayContent; + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; + + int curTokenIndex = mWallpaperTokens.size(); + while (curTokenIndex > 0) { + curTokenIndex--; + WindowToken token = mWallpaperTokens.get(curTokenIndex); + if (token.hidden == visible) { + token.hidden = !visible; + // Need to do a layout to ensure the wallpaper now has the + // correct size. + getDefaultDisplayContentLocked().layoutNeeded = true; + } + + int curWallpaperIndex = token.windows.size(); + while (curWallpaperIndex > 0) { + curWallpaperIndex--; + WindowState wallpaper = token.windows.get(curWallpaperIndex); + if (visible) { + updateWallpaperOffsetLocked(wallpaper, dw, dh, false); + } + + dispatchWallpaperVisibility(wallpaper, visible); + } + } + } + + public int addWindow(Session session, IWindow client, int seq, + WindowManager.LayoutParams attrs, int viewVisibility, int displayId, + Rect outContentInsets, InputChannel outInputChannel) { + int[] appOp = new int[1]; + int res = mPolicy.checkAddPermission(attrs, appOp); + if (res != WindowManagerGlobal.ADD_OKAY) { + return res; + } + + boolean reportNewConfig = false; + WindowState attachedWindow = null; + WindowState win = null; + long origId; + final int type = attrs.type; + + synchronized(mWindowMap) { + if (!mDisplayReady) { + throw new IllegalStateException("Display has not been initialialized"); + } + + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent == null) { + Slog.w(TAG, "Attempted to add window to a display that does not exist: " + + displayId + ". Aborting."); + return WindowManagerGlobal.ADD_INVALID_DISPLAY; + } + if (!displayContent.hasAccess(session.mUid)) { + Slog.w(TAG, "Attempted to add window to a display for which the application " + + "does not have access: " + displayId + ". Aborting."); + return WindowManagerGlobal.ADD_INVALID_DISPLAY; + } + + if (mWindowMap.containsKey(client.asBinder())) { + Slog.w(TAG, "Window " + client + " is already added"); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } + + if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { + attachedWindow = windowForClientLocked(null, attrs.token, false); + if (attachedWindow == null) { + Slog.w(TAG, "Attempted to add window with token that is not a window: " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; + } + if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW + && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { + Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; + } + } + + if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { + Slog.w(TAG, "Attempted to add private presentation window to a non-private display. Aborting."); + return WindowManagerGlobal.ADD_PERMISSION_DENIED; + } + + boolean addToken = false; + WindowToken token = mTokenMap.get(attrs.token); + if (token == null) { + if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { + Slog.w(TAG, "Attempted to add application window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + if (type == TYPE_INPUT_METHOD) { + Slog.w(TAG, "Attempted to add input method window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + if (type == TYPE_WALLPAPER) { + Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + if (type == TYPE_DREAM) { + Slog.w(TAG, "Attempted to add Dream window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + token = new WindowToken(this, attrs.token, -1, false); + addToken = true; + } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { + AppWindowToken atoken = token.appWindowToken; + if (atoken == null) { + Slog.w(TAG, "Attempted to add window with non-application token " + + token + ". Aborting."); + return WindowManagerGlobal.ADD_NOT_APP_TOKEN; + } else if (atoken.removed) { + Slog.w(TAG, "Attempted to add window with exiting application token " + + token + ". Aborting."); + return WindowManagerGlobal.ADD_APP_EXITING; + } + if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { + // No need for this guy! + if (localLOGV) Slog.v( + TAG, "**** NO NEED TO START: " + attrs.getTitle()); + return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; + } + } else if (type == TYPE_INPUT_METHOD) { + if (token.windowType != TYPE_INPUT_METHOD) { + Slog.w(TAG, "Attempted to add input method window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (type == TYPE_WALLPAPER) { + if (token.windowType != TYPE_WALLPAPER) { + Slog.w(TAG, "Attempted to add wallpaper window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (type == TYPE_DREAM) { + if (token.windowType != TYPE_DREAM) { + Slog.w(TAG, "Attempted to add Dream window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } + + win = new WindowState(this, session, client, token, + attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); + if (win.mDeathRecipient == null) { + // Client has apparently died, so there is no reason to + // continue. + Slog.w(TAG, "Adding window client " + client.asBinder() + + " that is dead, aborting."); + return WindowManagerGlobal.ADD_APP_EXITING; + } + + mPolicy.adjustWindowParamsLw(win.mAttrs); + win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); + + res = mPolicy.prepareAddWindowLw(win, attrs); + if (res != WindowManagerGlobal.ADD_OKAY) { + return res; + } + + if (outInputChannel != null && (attrs.inputFeatures + & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { + String name = win.makeInputChannelName(); + InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); + win.setInputChannel(inputChannels[0]); + inputChannels[1].transferTo(outInputChannel); + + mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); + } + + // From now on, no exceptions or errors allowed! + + res = WindowManagerGlobal.ADD_OKAY; + + origId = Binder.clearCallingIdentity(); + + if (addToken) { + mTokenMap.put(attrs.token, token); + } + win.attach(); + mWindowMap.put(client.asBinder(), win); + if (win.mAppOp != AppOpsManager.OP_NONE) { + if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage()) + != AppOpsManager.MODE_ALLOWED) { + win.setAppOpVisibilityLw(false); + } + } + + if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { + token.appWindowToken.startingWindow = win; + if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken + + " startingWindow=" + win); + Message m = mH.obtainMessage(H.REMOVE_STARTING_TIMEOUT, token.appWindowToken); + mH.sendMessageDelayed(m, STARTING_WINDOW_TIMEOUT_DURATION); + } + + boolean imMayMove = true; + + if (type == TYPE_INPUT_METHOD) { + win.mGivenInsetsPending = true; + mInputMethodWindow = win; + addInputMethodWindowToListLocked(win); + imMayMove = false; + } else if (type == TYPE_INPUT_METHOD_DIALOG) { + mInputMethodDialogs.add(win); + addWindowToListInOrderLocked(win, true); + moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); + imMayMove = false; + } else { + addWindowToListInOrderLocked(win, true); + if (type == TYPE_WALLPAPER) { + mLastWallpaperTimeoutTime = 0; + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (mWallpaperTarget != null + && mWallpaperTarget.mLayer >= win.mBaseLayer) { + // If there is currently a wallpaper being shown, and + // the base layer of the new window is below the current + // layer of the target window, then adjust the wallpaper. + // This is to avoid a new window being placed between the + // wallpaper and its target. + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } + } + + win.mWinAnimator.mEnterAnimationPending = true; + + if (displayContent.isDefaultDisplay) { + mPolicy.getContentInsetHintLw(attrs, outContentInsets); + } else { + outContentInsets.setEmpty(); + } + + if (mInTouchMode) { + res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; + } + if (win.mAppToken == null || !win.mAppToken.clientHidden) { + res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + } + + mInputMonitor.setUpdateInputWindowsNeededLw(); + + boolean focusChanged = false; + if (win.canReceiveKeys()) { + focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, + false /*updateInputWindows*/); + if (focusChanged) { + imMayMove = false; + } + } + + if (imMayMove) { + moveInputMethodWindowsIfNeededLocked(false); + } + + assignLayersLocked(displayContent.getWindowList()); + // Don't do layout here, the window must call + // relayout to be displayed, so we'll do it there. + + 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 (win.mAttrs.type == TYPE_APPLICATION_STARTING) { + if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win); + removeStartingWindowTimeout(win.mAppToken); + } + + if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v( + TAG, "Remove " + win + " client=" + + Integer.toHexString(System.identityHashCode(win.mClient.asBinder())) + + ", surface=" + win.mWinAnimator.mSurfaceControl + " Callers=" + + Debug.getCallers(4)); + + final long origId = Binder.clearCallingIdentity(); + + win.disposeInputChannel(); + + if (DEBUG_APP_TRANSITIONS) Slog.v( + TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurfaceControl + + " mExiting=" + win.mExiting + + " isAnimating=" + win.mWinAnimator.isAnimating() + + " app-animation=" + + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null) + + " inPendingTransaction=" + + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false) + + " mDisplayFrozen=" + mDisplayFrozen); + // Visibility of the removed window. Will be used later to update orientation later on. + boolean wasVisible = false; + // First, see if we need to run an animation. If we do, we have + // to hold off on removing the window until the animation is done. + // If the display is frozen, just remove immediately, since the + // animation wouldn't be seen. + if (win.mHasSurface && okToDisplay()) { + // If we are not currently running the exit animation, we + // need to see about starting one. + wasVisible = win.isWinVisibleLw(); + if (wasVisible) { + + int transit = WindowManagerPolicy.TRANSIT_EXIT; + if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { + transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; + } + // Try starting an animation. + if (win.mWinAnimator.applyAnimationLocked(transit, false)) { + win.mExiting = true; + } + //TODO (multidisplay): Magnification is supported only for the default display. + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, transit); + } + } + if (win.mExiting || win.mWinAnimator.isAnimating()) { + // The exit animation is running... wait for it! + //Slog.i(TAG, "*** Running exit animation..."); + win.mExiting = true; + win.mRemoveOnExit = true; + win.mDisplayContent.layoutNeeded = true; + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/); + performLayoutAndPlaceSurfacesLocked(); + 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 && 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()); + if (win.mAppOp != AppOpsManager.OP_NONE) { + mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); + } + + final WindowList windows = win.getWindowList(); + windows.remove(win); + mPendingRemove.remove(win); + mResizingWindows.remove(win); + mWindowsChanged = true; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win); + + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { + mInputMethodDialogs.remove(win); + } + + final WindowToken token = win.mToken; + final AppWindowToken atoken = win.mAppToken; + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + win + " from " + token); + token.windows.remove(win); + if (atoken != null) { + atoken.allAppWindows.remove(win); + } + if (localLOGV) Slog.v( + TAG, "**** Removing window " + win + ": count=" + + token.windows.size()); + if (token.windows.size() == 0) { + if (!token.explicit) { + mTokenMap.remove(token.token); + } else if (atoken != null) { + atoken.firstWindowDrawn = false; + } + } + + if (atoken != null) { + if (atoken.startingWindow == win) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling startingWindow " + win); + removeStartingWindowTimeout(atoken); + atoken.startingWindow = null; + } else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) { + // If this is the last window and we had requested a starting + // transition window, well there is no point now. + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling last startingWindow"); + atoken.startingData = null; + } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) { + // If this is the last window except for a starting transition + // window, we need to get rid of the starting transition. + scheduleRemoveStartingWindow(atoken); + } + } + + if (win.mAttrs.type == TYPE_WALLPAPER) { + mLastWallpaperTimeoutTime = 0; + getDefaultDisplayContentLocked().pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { + getDefaultDisplayContentLocked().pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } + + if (!mInLayout) { + assignLayersLocked(windows); + win.mDisplayContent.layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + if (win.mAppToken != null) { + win.mAppToken.updateReportedVisibilityLocked(); + } + } + + mInputMonitor.updateInputWindowsLw(true /*force*/); + } + + public void updateAppOpsState() { + synchronized(mWindowMap) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState win = windows.get(winNdx); + if (win.mAppOp != AppOpsManager.OP_NONE) { + final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(), + win.getOwningPackage()); + win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED); + } + } + } + } + } + + static void logSurface(WindowState w, String msg, RuntimeException where) { + String str = " SURFACE " + msg + ": " + w; + if (where != null) { + Slog.i(TAG, str, where); + } else { + Slog.i(TAG, str); + } + } + + static void logSurface(SurfaceControl s, String title, String msg, RuntimeException where) { + String str = " SURFACE " + s + ": " + msg + " / " + title; + if (where != null) { + Slog.i(TAG, str, where); + } else { + Slog.i(TAG, str); + } + } + + void setTransparentRegionWindow(Session session, IWindow client, Region region) { + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mWindowMap) { + WindowState w = windowForClientLocked(session, client, false); + if ((w != null) && w.mHasSurface) { + w.mWinAnimator.setTransparentRegionHintLocked(region); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + void setInsetsWindow(Session session, IWindow client, + int touchableInsets, Rect contentInsets, + Rect visibleInsets, Region touchableRegion) { + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mWindowMap) { + WindowState w = windowForClientLocked(session, client, false); + if (w != null) { + w.mGivenInsetsPending = false; + w.mGivenContentInsets.set(contentInsets); + w.mGivenVisibleInsets.set(visibleInsets); + w.mGivenTouchableRegion.set(touchableRegion); + w.mTouchableInsets = touchableInsets; + if (w.mGlobalScale != 1) { + w.mGivenContentInsets.scale(w.mGlobalScale); + w.mGivenVisibleInsets.scale(w.mGlobalScale); + w.mGivenTouchableRegion.scale(w.mGlobalScale); + } + w.mDisplayContent.layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + public void getWindowDisplayFrame(Session session, IWindow client, + Rect outDisplayFrame) { + synchronized(mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + outDisplayFrame.setEmpty(); + return; + } + outDisplayFrame.set(win.mDisplayFrame); + } + } + + public void setWindowWallpaperPositionLocked(WindowState window, float x, float y, + float xStep, float yStep) { + if (window.mWallpaperX != x || window.mWallpaperY != y) { + window.mWallpaperX = x; + window.mWallpaperY = y; + window.mWallpaperXStep = xStep; + window.mWallpaperYStep = yStep; + updateWallpaperOffsetLocked(window, true); + } + } + + void wallpaperCommandComplete(IBinder window, Bundle result) { + synchronized (mWindowMap) { + if (mWaitingOnWallpaper != null && + mWaitingOnWallpaper.mClient.asBinder() == window) { + mWaitingOnWallpaper = null; + mWindowMap.notifyAll(); + } + } + } + + public Bundle sendWindowWallpaperCommandLocked(WindowState window, + String action, int x, int y, int z, Bundle extras, boolean sync) { + if (window == mWallpaperTarget || window == mLowerWallpaperTarget + || window == mUpperWallpaperTarget) { + boolean doWait = sync; + int curTokenIndex = mWallpaperTokens.size(); + while (curTokenIndex > 0) { + curTokenIndex--; + WindowToken token = mWallpaperTokens.get(curTokenIndex); + int curWallpaperIndex = token.windows.size(); + while (curWallpaperIndex > 0) { + curWallpaperIndex--; + WindowState wallpaper = token.windows.get(curWallpaperIndex); + try { + wallpaper.mClient.dispatchWallpaperCommand(action, + x, y, z, extras, sync); + // We only want to be synchronous with one wallpaper. + sync = false; + } catch (RemoteException e) { + } + } + } + + if (doWait) { + // XXX Need to wait for result. + } + } + + return null; + } + + public void setUniverseTransformLocked(WindowState window, float alpha, + float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy) { + Transformation transform = window.mWinAnimator.mUniverseTransform; + transform.setAlpha(alpha); + Matrix matrix = transform.getMatrix(); + matrix.getValues(mTmpFloats); + mTmpFloats[Matrix.MTRANS_X] = offx; + mTmpFloats[Matrix.MTRANS_Y] = offy; + mTmpFloats[Matrix.MSCALE_X] = dsdx; + mTmpFloats[Matrix.MSKEW_Y] = dtdx; + mTmpFloats[Matrix.MSKEW_X] = dsdy; + mTmpFloats[Matrix.MSCALE_Y] = dtdy; + matrix.setValues(mTmpFloats); + final DisplayInfo displayInfo = window.mDisplayContent.getDisplayInfo(); + final RectF dispRect = new RectF(0, 0, + displayInfo.logicalWidth, displayInfo.logicalHeight); + matrix.mapRect(dispRect); + window.mGivenTouchableRegion.set(0, 0, + displayInfo.logicalWidth, displayInfo.logicalHeight); + window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top, + (int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE); + window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; + window.mDisplayContent.layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + + public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { + synchronized (mWindowMap) { + if (mDisplayMagnifier != null) { + WindowState window = mWindowMap.get(token); + //TODO (multidisplay): Magnification is supported only for the default display. + if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate); + } + } + } + } + + public IWindowId getWindowId(IBinder token) { + synchronized (mWindowMap) { + WindowState window = mWindowMap.get(token); + return window != null ? window.mWindowId : null; + } + } + + public int relayoutWindow(Session session, IWindow client, int seq, + WindowManager.LayoutParams attrs, int requestedWidth, + int requestedHeight, int viewVisibility, int flags, + Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, + Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { + boolean toBeDisplayed = false; + boolean inTouchMode; + boolean configChanged; + boolean surfaceChanged = false; + boolean animating; + + // if they don't have this permission, mask out the status bar bits + int systemUiVisibility = 0; + if (attrs != null) { + systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility); + if ((systemUiVisibility & StatusBarManager.DISABLE_MASK) != 0) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + systemUiVisibility &= ~StatusBarManager.DISABLE_MASK; + } + } + } + long origId = Binder.clearCallingIdentity(); + + synchronized(mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return 0; + } + WindowStateAnimator winAnimator = win.mWinAnimator; + if (win.mRequestedWidth != requestedWidth + || win.mRequestedHeight != requestedHeight) { + win.mLayoutNeeded = true; + win.mRequestedWidth = requestedWidth; + win.mRequestedHeight = requestedHeight; + } + if (attrs != null && seq == win.mSeq) { + win.mSystemUiVisibility = systemUiVisibility; + } + + if (attrs != null) { + mPolicy.adjustWindowParamsLw(attrs); + } + + winAnimator.mSurfaceDestroyDeferred = + (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0; + + int attrChanges = 0; + int flagChanges = 0; + if (attrs != null) { + if (win.mAttrs.type != attrs.type) { + throw new IllegalArgumentException( + "Window type can not be changed after the window is added."); + } + flagChanges = win.mAttrs.flags ^= attrs.flags; + attrChanges = win.mAttrs.copyFrom(attrs); + if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED + | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) { + win.mLayoutNeeded = true; + } + } + + if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility + + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); + + win.mEnforceSizeCompat = + (win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0; + + if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { + winAnimator.mAlpha = attrs.alpha; + } + + final boolean scaledWindow = + ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0); + + if (scaledWindow) { + // requested{Width|Height} Surface's physical size + // attrs.{width|height} Size on screen + win.mHScale = (attrs.width != requestedWidth) ? + (attrs.width / (float)requestedWidth) : 1.0f; + win.mVScale = (attrs.height != requestedHeight) ? + (attrs.height / (float)requestedHeight) : 1.0f; + } else { + win.mHScale = win.mVScale = 1; + } + + boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0; + + final boolean isDefaultDisplay = win.isDefaultDisplay(); + boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility + || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0) + || (!win.mRelayoutCalled)); + + boolean wallpaperMayMove = win.mViewVisibility != viewVisibility + && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; + wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0; + + win.mRelayoutCalled = true; + final int oldVisibility = win.mViewVisibility; + win.mViewVisibility = viewVisibility; + if (DEBUG_SCREEN_ON) { + RuntimeException stack = new RuntimeException(); + stack.fillInStackTrace(); + Slog.i(TAG, "Relayout " + win + ": oldVis=" + oldVisibility + + " newVis=" + viewVisibility, stack); + } + if (viewVisibility == View.VISIBLE && + (win.mAppToken == null || !win.mAppToken.clientHidden)) { + toBeDisplayed = !win.isVisibleLw(); + if (win.mExiting) { + winAnimator.cancelExitAnimationForNextAnimationLocked(); + win.mExiting = false; + } + if (win.mDestroying) { + win.mDestroying = false; + mDestroySurface.remove(win); + } + if (oldVisibility == View.GONE) { + winAnimator.mEnterAnimationPending = true; + } + if (toBeDisplayed) { + if (win.isDrawnLw() && okToDisplay()) { + winAnimator.applyEnterAnimationLocked(); + } + if ((win.mAttrs.flags + & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) { + if (DEBUG_VISIBILITY) Slog.v(TAG, + "Relayout window turning screen on: " + win); + win.mTurnOnScreen = true; + } + if (win.isConfigChanged()) { + if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + win + + " visible with new config: " + mCurConfiguration); + outConfig.setTo(mCurConfiguration); + } + } + if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) { + // To change the format, we need to re-build the surface. + winAnimator.destroySurfaceLocked(); + toBeDisplayed = true; + surfaceChanged = true; + } + try { + if (!win.mHasSurface) { + surfaceChanged = true; + } + SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); + if (surfaceControl != null) { + outSurface.copyFrom(surfaceControl); + if (SHOW_TRANSACTIONS) Slog.i(TAG, + " OUT SURFACE " + outSurface + ": copied"); + } else { + // For some reason there isn't a surface. Clear the + // caller's object so they see the same state. + outSurface.release(); + } + } catch (Exception e) { + mInputMonitor.updateInputWindowsLw(true /*force*/); + + Slog.w(TAG, "Exception thrown when creating surface for client " + + client + " (" + win.mAttrs.getTitle() + ")", + e); + Binder.restoreCallingIdentity(origId); + return 0; + } + if (toBeDisplayed) { + focusMayChange = isDefaultDisplay; + } + if (win.mAttrs.type == TYPE_INPUT_METHOD + && mInputMethodWindow == null) { + mInputMethodWindow = win; + imMayMove = true; + } + if (win.mAttrs.type == TYPE_BASE_APPLICATION + && win.mAppToken != null + && win.mAppToken.startingWindow != null) { + // Special handling of starting window over the base + // window of the app: propagate lock screen flags to it, + // to provide the correct semantics while starting. + final int mask = + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; + WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs; + sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask); + } + } else { + winAnimator.mEnterAnimationPending = false; + if (winAnimator.mSurfaceControl != null) { + if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win + + ": mExiting=" + win.mExiting); + // If we are not currently running the exit animation, we + // need to see about starting one. + if (!win.mExiting) { + surfaceChanged = true; + // Try starting an animation; if there isn't one, we + // can destroy the surface right away. + int transit = WindowManagerPolicy.TRANSIT_EXIT; + if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { + transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; + } + if (win.isWinVisibleLw() && + winAnimator.applyAnimationLocked(transit, false)) { + focusMayChange = isDefaultDisplay; + win.mExiting = true; + } else if (win.mWinAnimator.isAnimating()) { + // Currently in a hide animation... turn this into + // an exit. + win.mExiting = true; + } else if (win == mWallpaperTarget) { + // If the wallpaper is currently behind this + // window, we need to change both of them inside + // of a transaction to avoid artifacts. + win.mExiting = true; + win.mWinAnimator.mAnimating = true; + } else { + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } + winAnimator.destroySurfaceLocked(); + } + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, transit); + } + } + } + + outSurface.release(); + if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win); + } + + if (focusMayChange) { + //System.out.println("Focus may change: " + win.mAttrs.getTitle()); + if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/)) { + imMayMove = false; + } + //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); + } + + // updateFocusedWindowLocked() already assigned layers so we only need to + // reassign them at this point if the IM window state gets shuffled + if (imMayMove && (moveInputMethodWindowsIfNeededLocked(false) || toBeDisplayed)) { + // Little hack here -- we -should- be able to rely on the + // function to return true if the IME has moved and needs + // its layer recomputed. However, if the IME was hidden + // and isn't actually moved in the list, its layer may be + // out of data so we make sure to recompute it. + assignLayersLocked(win.getWindowList()); + } + + if (wallpaperMayMove) { + getDefaultDisplayContentLocked().pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } + + win.mDisplayContent.layoutNeeded = true; + win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; + configChanged = updateOrientationFromAppTokensLocked(false); + performLayoutAndPlaceSurfacesLocked(); + if (toBeDisplayed && win.mIsWallpaper) { + DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); + updateWallpaperOffsetLocked(win, + displayInfo.logicalWidth, displayInfo.logicalHeight, false); + } + if (win.mAppToken != null) { + win.mAppToken.updateReportedVisibilityLocked(); + } + outFrame.set(win.mCompatFrame); + outOverscanInsets.set(win.mOverscanInsets); + outContentInsets.set(win.mContentInsets); + outVisibleInsets.set(win.mVisibleInsets); + if (localLOGV) Slog.v( + TAG, "Relayout given client " + client.asBinder() + + ", requestedWidth=" + requestedWidth + + ", requestedHeight=" + requestedHeight + + ", viewVisibility=" + viewVisibility + + "\nRelayout returning frame=" + outFrame + + ", surface=" + outSurface); + + if (localLOGV || DEBUG_FOCUS) Slog.v( + TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange); + + inTouchMode = mInTouchMode; + animating = mAnimator.mAnimating && win.mWinAnimator.isAnimating(); + if (animating && !mRelayoutWhileAnimating.contains(win)) { + mRelayoutWhileAnimating.add(win); + } + + mInputMonitor.updateInputWindowsLw(true /*force*/); + + if (DEBUG_LAYOUT) { + Slog.v(TAG, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString()); + } + } + + if (configChanged) { + sendNewConfiguration(); + } + + Binder.restoreCallingIdentity(origId); + + return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0) + | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0) + | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0) + | (animating ? WindowManagerGlobal.RELAYOUT_RES_ANIMATING : 0); + } + + public void performDeferredDestroyWindow(Session session, IWindow client) { + long origId = Binder.clearCallingIdentity(); + + try { + synchronized (mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return; + } + win.mWinAnimator.destroyDeferredSurfaceLocked(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + public boolean outOfMemoryWindow(Session session, IWindow client) { + long origId = Binder.clearCallingIdentity(); + + try { + synchronized (mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return false; + } + return reclaimSomeSurfaceMemoryLocked(win.mWinAnimator, "from-client", false); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + public void finishDrawingWindow(Session session, IWindow client) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win != null && win.mWinAnimator.finishDrawingLocked()) { + if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { + getDefaultDisplayContentLocked().pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } + win.mDisplayContent.layoutNeeded = true; + requestTraversalLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void getWindowFrame(IBinder token, Rect outBounds) { + if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, + "getWindowInfo()")) { + throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); + } + synchronized (mWindowMap) { + WindowState windowState = mWindowMap.get(token); + if (windowState != null) { + outBounds.set(windowState.mFrame); + } else { + outBounds.setEmpty(); + } + } + } + + @Override + public void setMagnificationSpec(MagnificationSpec spec) { + if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, + "setMagnificationSpec()")) { + throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); + } + synchronized (mWindowMap) { + if (mDisplayMagnifier != null) { + mDisplayMagnifier.setMagnificationSpecLocked(spec); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + if (Binder.getCallingPid() != android.os.Process.myPid()) { + spec.recycle(); + } + } + + @Override + public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { + if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, + "getCompatibleMagnificationSpecForWindow()")) { + throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); + } + synchronized (mWindowMap) { + WindowState windowState = mWindowMap.get(windowToken); + if (windowState == null) { + return null; + } + MagnificationSpec spec = null; + if (mDisplayMagnifier != null) { + spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState); + } + if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { + return null; + } + spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec); + spec.scale *= windowState.mGlobalScale; + return spec; + } + } + + @Override + public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) { + if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, + "setMagnificationCallbacks()")) { + throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); + } + synchronized (mWindowMap) { + if (mDisplayMagnifier == null) { + mDisplayMagnifier = new DisplayMagnifier(this, callbacks); + } else { + if (callbacks == null) { + if (mDisplayMagnifier != null) { + mDisplayMagnifier.destroyLocked(); + mDisplayMagnifier = null; + } + } else { + throw new IllegalStateException("Magnification callbacks already set!"); + } + } + } + } + + private boolean applyAnimationLocked(AppWindowToken atoken, + WindowManager.LayoutParams lp, int transit, boolean enter) { + // Only apply an animation if the display isn't frozen. If it is + // frozen, there is no reason to animate and it can cause strange + // artifacts when we unfreeze the display if some different animation + // is running. + if (okToDisplay()) { + DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); + final int width = displayInfo.appWidth; + final int height = displayInfo.appHeight; + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + + atoken); + Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height); + if (a != null) { + if (DEBUG_ANIM) { + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e); + } + atoken.mAppAnimator.setAnimation(a, width, height); + } + } else { + atoken.mAppAnimator.clearAnimation(); + } + + return atoken.mAppAnimator.animation != null; + } + + // ------------------------------------------------------------- + // Application Window Tokens + // ------------------------------------------------------------- + + public void validateAppTokens(int stackId, List<TaskGroup> tasks) { + synchronized (mWindowMap) { + int t = tasks.size() - 1; + if (t < 0) { + Slog.w(TAG, "validateAppTokens: empty task list"); + return; + } + + TaskGroup task = tasks.get(0); + int taskId = task.taskId; + Task targetTask = mTaskIdToTask.get(taskId); + DisplayContent displayContent = targetTask.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "validateAppTokens: no Display for taskId=" + taskId); + return; + } + + final ArrayList<Task> localTasks = mStackIdToStack.get(stackId).getTasks(); + int taskNdx; + for (taskNdx = localTasks.size() - 1; taskNdx >= 0 && t >= 0; --taskNdx, --t) { + AppTokenList localTokens = localTasks.get(taskNdx).mAppTokens; + task = tasks.get(t); + List<IApplicationToken> tokens = task.tokens; + + DisplayContent lastDisplayContent = displayContent; + displayContent = mTaskIdToTask.get(taskId).getDisplayContent(); + if (displayContent != lastDisplayContent) { + Slog.w(TAG, "validateAppTokens: displayContent changed in TaskGroup list!"); + return; + } + + int tokenNdx; + int v; + for (tokenNdx = localTokens.size() - 1, v = task.tokens.size() - 1; + tokenNdx >= 0 && v >= 0; ) { + final AppWindowToken atoken = localTokens.get(tokenNdx); + if (atoken.removed) { + --tokenNdx; + continue; + } + if (tokens.get(v) != atoken.token) { + break; + } + --tokenNdx; + v--; + } + + if (tokenNdx >= 0 || v >= 0) { + break; + } + } + + if (taskNdx >= 0 || t >= 0) { + Slog.w(TAG, "validateAppTokens: Mismatch! ActivityManager=" + tasks); + Slog.w(TAG, "validateAppTokens: Mismatch! WindowManager=" + localTasks); + Slog.w(TAG, "validateAppTokens: Mismatch! Callers=" + Debug.getCallers(4)); + } + } + } + + public void validateStackOrder(Integer[] remoteStackIds) { + // TODO: + } + + boolean checkCallingPermission(String permission, String func) { + // Quick check: if the calling permission is me, it's all okay. + if (Binder.getCallingPid() == Process.myPid()) { + return true; + } + + if (mContext.checkCallingPermission(permission) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + String msg = "Permission Denial: " + func + " from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + permission; + Slog.w(TAG, msg); + return false; + } + + boolean okToDisplay() { + return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOnFully(); + } + + AppWindowToken findAppWindowToken(IBinder token) { + WindowToken wtoken = mTokenMap.get(token); + if (wtoken == null) { + return null; + } + return wtoken.appWindowToken; + } + + @Override + public void addWindowToken(IBinder token, int type) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "addWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + WindowToken wtoken = mTokenMap.get(token); + if (wtoken != null) { + Slog.w(TAG, "Attempted to add existing input method token: " + token); + return; + } + wtoken = new WindowToken(this, token, type, true); + mTokenMap.put(token, wtoken); + if (type == TYPE_WALLPAPER) { + mWallpaperTokens.add(wtoken); + } + } + } + + @Override + public void removeWindowToken(IBinder token) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "removeWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + final long origId = Binder.clearCallingIdentity(); + synchronized(mWindowMap) { + DisplayContent displayContent = null; + WindowToken wtoken = mTokenMap.remove(token); + if (wtoken != null) { + boolean delayed = false; + if (!wtoken.hidden) { + final int N = wtoken.windows.size(); + boolean changed = false; + + for (int i=0; i<N; i++) { + WindowState win = wtoken.windows.get(i); + displayContent = win.mDisplayContent; + + if (win.mWinAnimator.isAnimating()) { + delayed = true; + } + + if (win.isVisibleNow()) { + win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, + false); + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null && win.isDefaultDisplay()) { + mDisplayMagnifier.onWindowTransitionLocked(win, + WindowManagerPolicy.TRANSIT_EXIT); + } + changed = true; + displayContent.layoutNeeded = true; + } + } + + wtoken.hidden = true; + + if (changed) { + performLayoutAndPlaceSurfacesLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, + false /*updateInputWindows*/); + } + + if (delayed) { + displayContent.mExitingTokens.add(wtoken); + } else if (wtoken.windowType == TYPE_WALLPAPER) { + mWallpaperTokens.remove(wtoken); + } + } + + mInputMonitor.updateInputWindowsLw(true /*force*/); + } else { + Slog.w(TAG, "Attempted to remove non-existing token: " + token); + } + } + Binder.restoreCallingIdentity(origId); + } + + private Task createTask(int taskId, int stackId, int userId, AppWindowToken atoken) { + final TaskStack stack = mStackIdToStack.get(stackId); + if (stack == null) { + throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId); + } + EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId); + Task task = new Task(atoken, stack, userId); + mTaskIdToTask.put(taskId, task); + stack.addTask(task, true); + return task; + } + + @Override + public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, + int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, + int configChanges) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "addAppToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + // Get the dispatching timeout here while we are not holding any locks so that it + // can be cached by the AppWindowToken. The timeout value is used later by the + // input dispatcher in code that does hold locks. If we did not cache the value + // here we would run the chance of introducing a deadlock between the window manager + // (which holds locks while updating the input dispatcher state) and the activity manager + // (which holds locks while querying the application token). + long inputDispatchingTimeoutNanos; + try { + inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L; + } catch (RemoteException ex) { + Slog.w(TAG, "Could not get dispatching timeout.", ex); + inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + } + + synchronized(mWindowMap) { + AppWindowToken atoken = findAppWindowToken(token.asBinder()); + if (atoken != null) { + Slog.w(TAG, "Attempted to add existing app token: " + token); + return; + } + atoken = new AppWindowToken(this, token); + atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; + atoken.groupId = taskId; + atoken.appFullscreen = fullscreen; + atoken.showWhenLocked = showWhenLocked; + atoken.requestedOrientation = requestedOrientation; + atoken.layoutConfigChanges = (configChanges & + (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0; + if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + + " to stack=" + stackId + " task=" + taskId + " at " + addPos); + + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + task = createTask(taskId, stackId, userId, atoken); + } else { + task.addAppToken(addPos, atoken); + } + + mTokenMap.put(token.asBinder(), atoken); + + // Application tokens start out hidden. + atoken.hidden = true; + atoken.hiddenRequested = true; + + //dump(); + } + } + + @Override + public void setAppGroupId(IBinder token, int groupId) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setAppGroupId()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + final AppWindowToken atoken = findAppWindowToken(token); + if (atoken == null) { + Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token); + return; + } + Task oldTask = mTaskIdToTask.get(atoken.groupId); + oldTask.removeAppToken(atoken); + + atoken.groupId = groupId; + Task newTask = mTaskIdToTask.get(groupId); + if (newTask == null) { + newTask = createTask(groupId, oldTask.mStack.mStackId, oldTask.mUserId, atoken); + } + newTask.mAppTokens.add(atoken); + } + } + + public int getOrientationFromWindowsLocked() { + if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) { + // If the display is frozen, some activities may be in the middle + // of restarting, and thus have removed their old window. If the + // window has the flag to hide the lock screen, then the lock screen + // can re-appear and inflict its own orientation on us. Keep the + // orientation stable until this all settles down. + return mLastWindowForcedOrientation; + } + + // TODO(multidisplay): Change to the correct display. + final WindowList windows = getDefaultWindowListLocked(); + int pos = windows.size() - 1; + while (pos >= 0) { + WindowState win = windows.get(pos); + pos--; + if (win.mAppToken != null) { + // We hit an application window. so the orientation will be determined by the + // app window. No point in continuing further. + return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + } + if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { + continue; + } + int req = win.mAttrs.screenOrientation; + if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || + (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ + continue; + } + + if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req); + return (mLastWindowForcedOrientation=req); + } + return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + } + + public int getOrientationFromAppTokensLocked() { + int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + boolean findingBehind = false; + boolean lastFullscreen = false; + // TODO: Multi window. + DisplayContent displayContent = getDefaultDisplayContentLocked(); + final ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int firstToken = tokens.size() - 1; + for (int tokenNdx = firstToken; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken atoken = tokens.get(tokenNdx); + + if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken); + + // if we're about to tear down this window and not seek for + // the behind activity, don't use it for orientation + if (!findingBehind + && (!atoken.hidden && atoken.hiddenRequested)) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken + + " -- going to hide"); + continue; + } + + if (tokenNdx == firstToken) { + // If we have hit a new Task, and the bottom + // of the previous group didn't explicitly say to use + // the orientation behind it, and the last app was + // full screen, then we'll stick with the + // user's orientation. + if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND + && lastFullscreen) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + + " -- end of group, return " + lastOrientation); + return lastOrientation; + } + } + + // We ignore any hidden applications on the top. + if (atoken.hiddenRequested || atoken.willBeHidden) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken + + " -- hidden on top"); + continue; + } + + if (tokenNdx == 0) { + // Last token in this task. + lastOrientation = atoken.requestedOrientation; + } + + int or = atoken.requestedOrientation; + // If this application is fullscreen, and didn't explicitly say + // to use the orientation behind it, then just take whatever + // orientation it has and ignores whatever is under it. + lastFullscreen = atoken.appFullscreen; + if (lastFullscreen + && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + + " -- full screen, return " + or); + return or; + } + // If this application has requested an explicit orientation, + // then use it. + if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + + " -- explicitly set, return " + or); + return or; + } + findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); + } + } + if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation"); + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + @Override + public Configuration updateOrientationFromAppTokens( + Configuration currentConfig, IBinder freezeThisOneIfNeeded) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "updateOrientationFromAppTokens()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + Configuration config = null; + long ident = Binder.clearCallingIdentity(); + + synchronized(mWindowMap) { + config = updateOrientationFromAppTokensLocked(currentConfig, + freezeThisOneIfNeeded); + } + + Binder.restoreCallingIdentity(ident); + return config; + } + + private Configuration updateOrientationFromAppTokensLocked( + Configuration currentConfig, IBinder freezeThisOneIfNeeded) { + Configuration config = null; + + if (updateOrientationFromAppTokensLocked(false)) { + if (freezeThisOneIfNeeded != null) { + AppWindowToken atoken = findAppWindowToken(freezeThisOneIfNeeded); + if (atoken != null) { + startAppFreezingScreenLocked(atoken, ActivityInfo.CONFIG_ORIENTATION); + } + } + config = computeNewConfigurationLocked(); + + } else if (currentConfig != null) { + // No obvious action we need to take, but if our current + // state mismatches the activity manager's, update it, + // disregarding font scale, which should remain set to + // the value of the previous configuration. + mTempConfiguration.setToDefaults(); + mTempConfiguration.fontScale = currentConfig.fontScale; + if (computeScreenConfigurationLocked(mTempConfiguration)) { + if (currentConfig.diff(mTempConfiguration) != 0) { + mWaitingForConfig = true; + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + displayContent.layoutNeeded = true; + int anim[] = new int[2]; + if (displayContent.isDimming()) { + anim[0] = anim[1] = 0; + } else { + mPolicy.selectRotationAnimationLw(anim); + } + startFreezingDisplayLocked(false, anim[0], anim[1]); + config = new Configuration(mTempConfiguration); + } + } + } + + return config; + } + + /* + * Determine the new desired orientation of the display, returning + * a non-null new Configuration if it has changed from the current + * orientation. IF TRUE IS RETURNED SOMEONE MUST CALL + * setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE + * SCREEN. This will typically be done for you if you call + * sendNewConfiguration(). + * + * The orientation is computed from non-application windows first. If none of + * the non-application windows specify orientation, the orientation is computed from + * application tokens. + * @see android.view.IWindowManager#updateOrientationFromAppTokens( + * android.os.IBinder) + */ + boolean updateOrientationFromAppTokensLocked(boolean inTransaction) { + long ident = Binder.clearCallingIdentity(); + try { + int req = getOrientationFromWindowsLocked(); + if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + req = getOrientationFromAppTokensLocked(); + } + + if (req != mForcedAppOrientation) { + mForcedAppOrientation = req; + //send a message to Policy indicating orientation change to take + //action like disabling/enabling sensors etc., + mPolicy.setCurrentOrientationLw(req); + if (updateRotationUncheckedLocked(inTransaction)) { + // changed + return true; + } + } + + return false; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public 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); + if (mWaitingForConfig) { + mWaitingForConfig = false; + mLastFinishedFreezeSource = "new-config"; + } + performLayoutAndPlaceSurfacesLocked(); + } + } + + @Override + public void setAppOrientation(IApplicationToken token, int requestedOrientation) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setAppOrientation()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + AppWindowToken atoken = findAppWindowToken(token.asBinder()); + if (atoken == null) { + Slog.w(TAG, "Attempted to set orientation of non-existing app token: " + token); + return; + } + + atoken.requestedOrientation = requestedOrientation; + } + } + + @Override + public int getAppOrientation(IApplicationToken token) { + synchronized(mWindowMap) { + AppWindowToken wtoken = findAppWindowToken(token.asBinder()); + if (wtoken == null) { + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + return wtoken.requestedOrientation; + } + } + + /** Call while in a Surface transaction. */ + void setFocusedStackLayer() { + mFocusedStackLayer = 0; + if (mFocusedApp != null) { + final WindowList windows = mFocusedApp.allAppWindows; + for (int i = windows.size() - 1; i >= 0; --i) { + final WindowState win = windows.get(i); + final int animLayer = win.mWinAnimator.mAnimLayer; + if (win.mAttachedWindow == null && win.isVisibleLw() && + animLayer > mFocusedStackLayer) { + mFocusedStackLayer = animLayer + LAYER_OFFSET_FOCUSED_STACK; + } + } + } + if (DEBUG_LAYERS) Slog.v(TAG, "Setting FocusedStackFrame to layer=" + + mFocusedStackLayer); + mFocusedStackFrame.setLayer(mFocusedStackLayer); + } + + void setFocusedStackFrame() { + final TaskStack stack; + if (mFocusedApp != null) { + Task task = mTaskIdToTask.get(mFocusedApp.groupId); + stack = task.mStack; + task.getDisplayContent().setTouchExcludeRegion(stack); + } else { + stack = null; + } + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setFocusedStackFrame"); + SurfaceControl.openTransaction(); + try { + if (stack == null) { + mFocusedStackFrame.setVisibility(false); + } else { + mFocusedStackFrame.setBounds(stack); + final boolean multipleStacks = !stack.isFullscreen(); + mFocusedStackFrame.setVisibility(multipleStacks); + } + } finally { + SurfaceControl.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> CLOSE TRANSACTION setFocusedStackFrame"); + } + } + + @Override + public void setFocusedApp(IBinder token, boolean moveFocusNow) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setFocusedApp()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + boolean changed = false; + if (token == null) { + if (DEBUG_FOCUS_LIGHT) 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; + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "Set focused app to: " + newFocus + + " old focus=" + mFocusedApp + " moveFocusNow=" + moveFocusNow); + mFocusedApp = newFocus; + if (changed) { + mInputMonitor.setFocusedAppLw(newFocus); + } + } + + if (moveFocusNow && changed) { + final long origId = Binder.clearCallingIdentity(); + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "prepareAppTransition()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + if (DEBUG_APP_TRANSITIONS) Slog.v( + TAG, "Prepare app transition: transit=" + transit + + " " + mAppTransition + + " alwaysKeepCurrent=" + alwaysKeepCurrent + + " Callers=" + Debug.getCallers(3)); + if (okToDisplay()) { + if (!mAppTransition.isTransitionSet() || mAppTransition.isTransitionNone()) { + mAppTransition.setAppTransition(transit); + } else if (!alwaysKeepCurrent) { + if (transit == AppTransition.TRANSIT_TASK_OPEN + && mAppTransition.isTransitionEqual( + AppTransition.TRANSIT_TASK_CLOSE)) { + // Opening a new task always supersedes a close for the anim. + mAppTransition.setAppTransition(transit); + } else if (transit == AppTransition.TRANSIT_ACTIVITY_OPEN + && mAppTransition.isTransitionEqual( + AppTransition.TRANSIT_ACTIVITY_CLOSE)) { + // Opening a new activity always supersedes a close for the anim. + mAppTransition.setAppTransition(transit); + } + } + mAppTransition.prepare(); + mStartingIconInTransition = false; + mSkipAppTransitionAnimation = false; + mH.removeMessages(H.APP_TRANSITION_TIMEOUT); + mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, 5000); + } + } + } + + @Override + public int getPendingAppTransition() { + return mAppTransition.getAppTransition(); + } + + @Override + public void overridePendingAppTransition(String packageName, + int enterAnim, int exitAnim, IRemoteCallback startedCallback) { + synchronized(mWindowMap) { + mAppTransition.overridePendingAppTransition(packageName, enterAnim, exitAnim, + startedCallback); + } + } + + @Override + public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, + int startHeight) { + synchronized(mWindowMap) { + mAppTransition.overridePendingAppTransitionScaleUp(startX, startY, startWidth, + startHeight); + } + } + + @Override + public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, + int startY, IRemoteCallback startedCallback, boolean scaleUp) { + synchronized(mWindowMap) { + mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY, + startedCallback, scaleUp); + } + } + + @Override + public void executeAppTransition() { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "executeAppTransition()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + if (DEBUG_APP_TRANSITIONS) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.w(TAG, "Execute app transition: " + mAppTransition, e); + } + if (mAppTransition.isTransitionSet()) { + mAppTransition.setReady(); + final long origId = Binder.clearCallingIdentity(); + performLayoutAndPlaceSurfacesLocked(); + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public void setAppStartingWindow(IBinder token, String pkg, + int theme, CompatibilityInfo compatInfo, + CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, + int windowFlags, IBinder transferFrom, boolean createIfNeeded) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setAppStartingWindow()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + if (DEBUG_STARTING_WINDOW) Slog.v( + TAG, "setAppStartingWindow: token=" + token + " pkg=" + pkg + + " transferFrom=" + transferFrom); + + AppWindowToken wtoken = findAppWindowToken(token); + if (wtoken == null) { + Slog.w(TAG, "Attempted to set icon of non-existing app token: " + token); + return; + } + + // If the display is frozen, we won't do anything until the + // actual window is displayed so there is no reason to put in + // the starting window. + if (!okToDisplay()) { + return; + } + + if (wtoken.startingData != null) { + return; + } + + if (transferFrom != null) { + AppWindowToken ttoken = findAppWindowToken(transferFrom); + if (ttoken != null) { + WindowState startingWindow = ttoken.startingWindow; + if (startingWindow != null) { + if (mStartingIconInTransition) { + // In this case, the starting icon has already + // been displayed, so start letting windows get + // shown immediately without any more transitions. + mSkipAppTransitionAnimation = true; + } + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, + "Moving existing starting " + startingWindow + " from " + ttoken + + " to " + wtoken); + final long origId = Binder.clearCallingIdentity(); + + // Transfer the starting window over to the new + // token. + wtoken.startingData = ttoken.startingData; + wtoken.startingView = ttoken.startingView; + wtoken.startingDisplayed = ttoken.startingDisplayed; + ttoken.startingDisplayed = false; + wtoken.startingWindow = startingWindow; + wtoken.reportedVisible = ttoken.reportedVisible; + ttoken.startingData = null; + ttoken.startingView = null; + ttoken.startingWindow = null; + ttoken.startingMoved = true; + startingWindow.mToken = wtoken; + startingWindow.mRootToken = wtoken; + startingWindow.mAppToken = wtoken; + startingWindow.mWinAnimator.mAppAnimator = wtoken.mAppAnimator; + + if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Removing starting window: " + startingWindow); + } + removeStartingWindowTimeout(ttoken); + startingWindow.getWindowList().remove(startingWindow); + mWindowsChanged = true; + if (DEBUG_ADD_REMOVE) Slog.v(TAG, + "Removing starting " + startingWindow + " from " + ttoken); + ttoken.windows.remove(startingWindow); + ttoken.allAppWindows.remove(startingWindow); + addWindowToListInOrderLocked(startingWindow, true); + + // Propagate other interesting state between the + // tokens. If the old token is displayed, we should + // immediately force the new one to be displayed. If + // it is animating, we need to move that animation to + // the new one. + if (ttoken.allDrawn) { + wtoken.allDrawn = true; + wtoken.deferClearAllDrawn = ttoken.deferClearAllDrawn; + } + if (ttoken.firstWindowDrawn) { + wtoken.firstWindowDrawn = true; + } + if (!ttoken.hidden) { + wtoken.hidden = false; + wtoken.hiddenRequested = false; + wtoken.willBeHidden = false; + } + if (wtoken.clientHidden != ttoken.clientHidden) { + wtoken.clientHidden = ttoken.clientHidden; + wtoken.sendAppVisibilityToClients(); + } + final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator; + final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator; + if (tAppAnimator.animation != null) { + wAppAnimator.animation = tAppAnimator.animation; + wAppAnimator.animating = tAppAnimator.animating; + wAppAnimator.animLayerAdjustment = tAppAnimator.animLayerAdjustment; + tAppAnimator.animation = null; + tAppAnimator.animLayerAdjustment = 0; + wAppAnimator.updateLayers(); + tAppAnimator.updateLayers(); + } + + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + true /*updateInputWindows*/); + getDefaultDisplayContentLocked().layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + Binder.restoreCallingIdentity(origId); + return; + } else if (ttoken.startingData != null) { + // The previous app was getting ready to show a + // starting window, but hasn't yet done so. Steal it! + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, + "Moving pending starting from " + ttoken + + " to " + wtoken); + wtoken.startingData = ttoken.startingData; + ttoken.startingData = null; + ttoken.startingMoved = true; + Message m = mH.obtainMessage(H.ADD_STARTING, wtoken); + // Note: we really want to do sendMessageAtFrontOfQueue() because we + // want to process the message ASAP, before any other queued + // messages. + mH.sendMessageAtFrontOfQueue(m); + return; + } + final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator; + final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator; + if (tAppAnimator.thumbnail != null) { + // The old token is animating with a thumbnail, transfer + // that to the new token. + if (wAppAnimator.thumbnail != null) { + wAppAnimator.thumbnail.destroy(); + } + wAppAnimator.thumbnail = tAppAnimator.thumbnail; + wAppAnimator.thumbnailX = tAppAnimator.thumbnailX; + wAppAnimator.thumbnailY = tAppAnimator.thumbnailY; + wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer; + wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation; + tAppAnimator.thumbnail = null; + } + } + } + + // There is no existing starting window, and the caller doesn't + // want us to create one, so that's it! + if (!createIfNeeded) { + return; + } + + // If this is a translucent window, then don't + // show a starting window -- the current effect (a full-screen + // opaque starting window that fades away to the real contents + // when it is ready) does not work for this. + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Checking theme of starting window: 0x" + + Integer.toHexString(theme)); + if (theme != 0) { + AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, + com.android.internal.R.styleable.Window, mCurrentUserId); + if (ent == null) { + // Whoops! App doesn't exist. Um. Okay. We'll just + // pretend like we didn't see that. + return; + } + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Translucent=" + + ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsTranslucent, false) + + " Floating=" + + ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsFloating, false) + + " ShowWallpaper=" + + ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowShowWallpaper, false)); + if (ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsTranslucent, false)) { + return; + } + if (ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsFloating, false)) { + return; + } + if (ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { + if (mWallpaperTarget == null) { + // If this theme is requesting a wallpaper, and the wallpaper + // is not curently visible, then this effectively serves as + // an opaque window and our starting window transition animation + // can still work. We just need to make sure the starting window + // is also showing the wallpaper. + windowFlags |= FLAG_SHOW_WALLPAPER; + } else { + return; + } + } + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Creating StartingData"); + mStartingIconInTransition = true; + wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel, + labelRes, icon, logo, windowFlags); + Message m = mH.obtainMessage(H.ADD_STARTING, wtoken); + // Note: we really want to do sendMessageAtFrontOfQueue() because we + // want to process the message ASAP, before any other queued + // messages. + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING"); + mH.sendMessageAtFrontOfQueue(m); + } + } + + @Override + 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; + } + } + + public void setAppFullscreen(IBinder token, boolean toOpaque) { + AppWindowToken atoken = findAppWindowToken(token); + if (atoken != null) { + atoken.appFullscreen = toOpaque; + requestTraversal(); + } + } + + boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, + boolean visible, int transit, boolean performLayout) { + boolean delayed = false; + + if (wtoken.clientHidden == visible) { + wtoken.clientHidden = !visible; + wtoken.sendAppVisibilityToClients(); + } + + wtoken.willBeHidden = false; + if (wtoken.hidden == visible) { + boolean changed = false; + if (DEBUG_APP_TRANSITIONS) Slog.v( + TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + + " performLayout=" + performLayout); + + boolean runningAppAnimation = false; + + if (transit != AppTransition.TRANSIT_UNSET) { + if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { + wtoken.mAppAnimator.animation = null; + } + if (applyAnimationLocked(wtoken, lp, transit, visible)) { + delayed = runningAppAnimation = true; + } + WindowState window = wtoken.findMainWindow(); + //TODO (multidisplay): Magnification is supported only for the default display. + if (window != null && mDisplayMagnifier != null + && window.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onAppWindowTransitionLocked(window, transit); + } + changed = true; + } + + final int N = wtoken.allAppWindows.size(); + for (int i=0; i<N; i++) { + WindowState win = wtoken.allAppWindows.get(i); + if (win == wtoken.startingWindow) { + continue; + } + + //Slog.i(TAG, "Window " + win + ": vis=" + win.isVisible()); + //win.dump(" "); + if (visible) { + if (!win.isVisibleNow()) { + if (!runningAppAnimation) { + win.mWinAnimator.applyAnimationLocked( + WindowManagerPolicy.TRANSIT_ENTER, true); + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, + WindowManagerPolicy.TRANSIT_ENTER); + } + } + changed = true; + win.mDisplayContent.layoutNeeded = true; + } + } else if (win.isVisibleNow()) { + if (!runningAppAnimation) { + win.mWinAnimator.applyAnimationLocked( + WindowManagerPolicy.TRANSIT_EXIT, false); + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, + WindowManagerPolicy.TRANSIT_EXIT); + } + } + changed = true; + win.mDisplayContent.layoutNeeded = true; + } + } + + wtoken.hidden = wtoken.hiddenRequested = !visible; + if (!visible) { + unsetAppFreezingScreenLocked(wtoken, true, true); + } else { + // If we are being set visible, and the starting window is + // not yet displayed, then make sure it doesn't get displayed. + WindowState swin = wtoken.startingWindow; + if (swin != null && !swin.isDrawnLw()) { + swin.mPolicyVisibility = false; + swin.mPolicyVisibilityAfterAnim = false; + } + } + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "setTokenVisibilityLocked: " + wtoken + + ": hidden=" + wtoken.hidden + " hiddenRequested=" + + wtoken.hiddenRequested); + + if (changed) { + mInputMonitor.setUpdateInputWindowsNeededLw(); + if (performLayout) { + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/); + performLayoutAndPlaceSurfacesLocked(); + } + mInputMonitor.updateInputWindowsLw(false /*force*/); + } + } + + if (wtoken.mAppAnimator.animation != null) { + delayed = true; + } + + for (int i = wtoken.allAppWindows.size() - 1; i >= 0 && !delayed; i--) { + if (wtoken.allAppWindows.get(i).mWinAnimator.isWindowAnimating()) { + delayed = true; + } + } + + return delayed; + } + + @Override + public void setAppVisibility(IBinder token, boolean visible) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setAppVisibility()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + AppWindowToken wtoken; + + synchronized(mWindowMap) { + wtoken = findAppWindowToken(token); + if (wtoken == null) { + Slog.w(TAG, "Attempted to set visibility of non-existing app token: " + token); + return; + } + + if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) { + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(TAG, "setAppVisibility(" + token + ", visible=" + visible + + "): " + mAppTransition + + " hidden=" + wtoken.hidden + + " hiddenRequested=" + wtoken.hiddenRequested, e); + } + + // If we are preparing an app transition, then delay changing + // the visibility of this token until we execute that transition. + if (okToDisplay() && mAppTransition.isTransitionSet()) { + wtoken.hiddenRequested = !visible; + + if (!wtoken.startingDisplayed) { + if (DEBUG_APP_TRANSITIONS) Slog.v( + TAG, "Setting dummy animation on: " + wtoken); + wtoken.mAppAnimator.setDummyAnimation(); + } + mOpeningApps.remove(wtoken); + mClosingApps.remove(wtoken); + wtoken.waitingToShow = wtoken.waitingToHide = false; + wtoken.inPendingTransaction = true; + if (visible) { + mOpeningApps.add(wtoken); + wtoken.startingMoved = false; + + // If the token is currently hidden (should be the + // common case), then we need to set up to wait for + // its windows to be ready. + if (wtoken.hidden) { + wtoken.allDrawn = false; + wtoken.deferClearAllDrawn = false; + wtoken.waitingToShow = true; + + if (wtoken.clientHidden) { + // In the case where we are making an app visible + // but holding off for a transition, we still need + // to tell the client to make its windows visible so + // they get drawn. Otherwise, we will wait on + // performing the transition until all windows have + // been drawn, they never will be, and we are sad. + wtoken.clientHidden = false; + wtoken.sendAppVisibilityToClients(); + } + } + } else { + mClosingApps.add(wtoken); + + // If the token is currently visible (should be the + // common case), then set up to wait for it to be hidden. + if (!wtoken.hidden) { + wtoken.waitingToHide = true; + } + } + return; + } + + final long origId = Binder.clearCallingIdentity(); + setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET, + true); + wtoken.updateReportedVisibilityLocked(); + Binder.restoreCallingIdentity(origId); + } + } + + void unsetAppFreezingScreenLocked(AppWindowToken wtoken, + boolean unfreezeSurfaceNow, boolean force) { + if (wtoken.mAppAnimator.freezingScreen) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + wtoken + + " force=" + force); + final int N = wtoken.allAppWindows.size(); + boolean unfrozeWindows = false; + for (int i=0; i<N; i++) { + WindowState w = wtoken.allAppWindows.get(i); + if (w.mAppFreezing) { + w.mAppFreezing = false; + if (w.mHasSurface && !w.mOrientationChanging) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "set mOrientationChanging of " + w); + w.mOrientationChanging = true; + mInnerFields.mOrientationChangeComplete = false; + } + w.mLastFreezeDuration = 0; + unfrozeWindows = true; + w.mDisplayContent.layoutNeeded = true; + } + } + if (force || unfrozeWindows) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken); + wtoken.mAppAnimator.freezingScreen = false; + wtoken.mAppAnimator.lastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); + mAppsFreezingScreen--; + mLastFinishedFreezeSource = wtoken; + } + if (unfreezeSurfaceNow) { + if (unfrozeWindows) { + performLayoutAndPlaceSurfacesLocked(); + } + stopFreezingDisplayLocked(); + } + } + } + + public void startAppFreezingScreenLocked(AppWindowToken wtoken, + int configChanges) { + if (DEBUG_ORIENTATION) { + RuntimeException e = null; + if (!HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.i(TAG, "Set freezing of " + wtoken.appToken + + ": hidden=" + wtoken.hidden + " freezing=" + + wtoken.mAppAnimator.freezingScreen, e); + } + if (!wtoken.hiddenRequested) { + if (!wtoken.mAppAnimator.freezingScreen) { + wtoken.mAppAnimator.freezingScreen = true; + wtoken.mAppAnimator.lastFreezeDuration = 0; + mAppsFreezingScreen++; + if (mAppsFreezingScreen == 1) { + startFreezingDisplayLocked(false, 0, 0); + mH.removeMessages(H.APP_FREEZE_TIMEOUT); + mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 5000); + } + } + final int N = wtoken.allAppWindows.size(); + for (int i=0; i<N; i++) { + WindowState w = wtoken.allAppWindows.get(i); + w.mAppFreezing = true; + } + } + } + + @Override + public void startAppFreezingScreen(IBinder token, int configChanges) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setAppFreezingScreen()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + if (configChanges == 0 && okToDisplay()) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping set freeze of " + token); + return; + } + + AppWindowToken wtoken = findAppWindowToken(token); + if (wtoken == null || wtoken.appToken == null) { + Slog.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken); + return; + } + final long origId = Binder.clearCallingIdentity(); + startAppFreezingScreenLocked(wtoken, configChanges); + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void stopAppFreezingScreen(IBinder token, boolean force) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setAppFreezingScreen()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized(mWindowMap) { + AppWindowToken wtoken = findAppWindowToken(token); + if (wtoken == null || wtoken.appToken == null) { + return; + } + final long origId = Binder.clearCallingIdentity(); + if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + token + + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.mAppAnimator.freezingScreen); + unsetAppFreezingScreenLocked(wtoken, true, force); + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void removeAppToken(IBinder token) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "removeAppToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + AppWindowToken wtoken = null; + AppWindowToken startingToken = null; + boolean delayed = false; + + final long origId = Binder.clearCallingIdentity(); + synchronized(mWindowMap) { + WindowToken basewtoken = mTokenMap.remove(token); + if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken); + delayed = setTokenVisibilityLocked(wtoken, null, false, + AppTransition.TRANSIT_UNSET, true); + wtoken.inPendingTransaction = false; + mOpeningApps.remove(wtoken); + wtoken.waitingToShow = false; + if (mClosingApps.contains(wtoken)) { + delayed = true; + } else if (mAppTransition.isTransitionSet()) { + mClosingApps.add(wtoken); + wtoken.waitingToHide = true; + delayed = true; + } + if (DEBUG_APP_TRANSITIONS) Slog.v( + TAG, "Removing app " + wtoken + " delayed=" + delayed + + " animation=" + wtoken.mAppAnimator.animation + + " animating=" + wtoken.mAppAnimator.animating); + final Task task = mTaskIdToTask.get(wtoken.groupId); + DisplayContent displayContent = task.getDisplayContent(); + if (delayed) { + // set the token aside because it has an active animation to be finished + if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, + "removeAppToken make exiting: " + wtoken); + displayContent.mExitingAppTokens.add(wtoken); + } else { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + wtoken.mAppAnimator.clearAnimation(); + wtoken.mAppAnimator.animating = false; + } + if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, + "removeAppToken: " + wtoken); + + if (task.removeAppToken(wtoken)) { + mTaskIdToTask.delete(wtoken.groupId); + } + wtoken.removed = true; + if (wtoken.startingData != null) { + startingToken = wtoken; + } + unsetAppFreezingScreenLocked(wtoken, true, true); + if (mFocusedApp == wtoken) { + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "Removing focused app token:" + wtoken); + mFocusedApp = null; + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); + mInputMonitor.setFocusedAppLw(null); + } + } else { + Slog.w(TAG, "Attempted to remove non-existing app token: " + token); + } + + if (!delayed && wtoken != null) { + wtoken.updateReportedVisibilityLocked(); + } + } + Binder.restoreCallingIdentity(origId); + + // Will only remove if startingToken non null. + scheduleRemoveStartingWindow(startingToken); + } + + void removeStartingWindowTimeout(AppWindowToken wtoken) { + if (wtoken != null) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) + + ": Remove starting window timeout " + wtoken + (wtoken != null ? + " startingWindow=" + wtoken.startingWindow : "")); + mH.removeMessages(H.REMOVE_STARTING_TIMEOUT, wtoken); + } + } + + void scheduleRemoveStartingWindow(AppWindowToken wtoken) { + if (wtoken != null && wtoken.startingWindow != null) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) + + ": Schedule remove starting " + wtoken + (wtoken != null ? + " startingWindow=" + wtoken.startingWindow : "")); + removeStartingWindowTimeout(wtoken); + Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken); + mH.sendMessage(m); + } + } + private boolean tmpRemoveAppWindowsLocked(WindowToken token) { + final int NW = token.windows.size(); + if (NW > 0) { + mWindowsChanged = true; + } + for (int i=0; i<NW; i++) { + WindowState win = token.windows.get(i); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win); + win.getWindowList().remove(win); + int j = win.mChildWindows.size(); + while (j > 0) { + j--; + WindowState cwin = win.mChildWindows.get(j); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, + "Tmp removing child window " + cwin); + cwin.getWindowList().remove(cwin); + } + } + return NW > 0; + } + + void dumpAppTokensLocked() { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + Slog.v(TAG, " Display " + displayContent.getDisplayId()); + final ArrayList<Task> tasks = displayContent.getTasks(); + int i = displayContent.numTokens(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + Slog.v(TAG, " #" + --i + ": " + wtoken.token); + } + } + } + } + + void dumpWindowsLocked() { + int i = 0; + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { + Slog.v(TAG, " #" + i++ + ": " + windows.get(winNdx)); + } + } + } + + private int findAppWindowInsertionPointLocked(AppWindowToken target) { + final int taskId = target.groupId; + Task targetTask = mTaskIdToTask.get(taskId); + if (targetTask == null) { + Slog.w(TAG, "findAppWindowInsertionPointLocked: no Task for " + target + " taskId=" + + taskId); + return 0; + } + DisplayContent displayContent = targetTask.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "findAppWindowInsertionPointLocked: no DisplayContent for " + target); + return 0; + } + final WindowList windows = displayContent.getWindowList(); + final int NW = windows.size(); + + boolean found = false; + final ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + final Task task = tasks.get(taskNdx); + if (!found && task.taskId != taskId) { + continue; + } + AppTokenList tokens = task.mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + if (!found && wtoken == target) { + found = true; + } + if (found) { + // Find the first app token below the new position that has + // a window displayed. + if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows in " + wtoken.token); + if (wtoken.sendingToBottom) { + if (DEBUG_REORDER) Slog.v(TAG, "Skipping token -- currently sending to bottom"); + continue; + } + for (int i = wtoken.windows.size() - 1; i >= 0; --i) { + WindowState win = wtoken.windows.get(i); + for (int j = win.mChildWindows.size() - 1; j >= 0; --j) { + WindowState cwin = win.mChildWindows.get(j); + if (cwin.mSubLayer >= 0) { + for (int pos = NW - 1; pos >= 0; pos--) { + if (windows.get(pos) == cwin) { + if (DEBUG_REORDER) Slog.v(TAG, + "Found child win @" + (pos + 1)); + return pos + 1; + } + } + } + } + for (int pos = NW - 1; pos >= 0; pos--) { + if (windows.get(pos) == win) { + if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos + 1)); + return pos + 1; + } + } + } + } + } + } + // Never put an app window underneath wallpaper. + for (int pos = NW - 1; pos >= 0; pos--) { + if (windows.get(pos).mIsWallpaper) { + if (DEBUG_REORDER) Slog.v(TAG, "Found wallpaper @" + pos); + return pos + 1; + } + } + return 0; + } + + private final int reAddWindowLocked(int index, WindowState win) { + final WindowList windows = win.getWindowList(); + final int NCW = win.mChildWindows.size(); + boolean added = false; + for (int j=0; j<NCW; j++) { + WindowState cwin = win.mChildWindows.get(j); + if (!added && cwin.mSubLayer >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at " + + index + ": " + cwin); + win.mRebuilding = false; + windows.add(index, win); + index++; + added = true; + } + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at " + + index + ": " + cwin); + cwin.mRebuilding = false; + windows.add(index, cwin); + index++; + } + if (!added) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at " + + index + ": " + win); + win.mRebuilding = false; + windows.add(index, win); + index++; + } + mWindowsChanged = true; + return index; + } + + private final int reAddAppWindowsLocked(final DisplayContent displayContent, int index, + WindowToken token) { + final int NW = token.windows.size(); + for (int i=0; i<NW; i++) { + final WindowState win = token.windows.get(i); + if (win.mDisplayContent == displayContent) { + index = reAddWindowLocked(index, win); + } + } + return index; + } + + void moveStackWindowsLocked(DisplayContent displayContent) { + // First remove all of the windows from the list. + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = numTokens - 1; tokenNdx >= 0; --tokenNdx) { + tmpRemoveAppWindowsLocked(tokens.get(tokenNdx)); + } + } + + // And now add them back at the correct place. + // Where to start adding? + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + int pos = findAppWindowInsertionPointLocked(tokens.get(0)); + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + if (wtoken != null) { + final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken); + if (newPos != pos) { + displayContent.layoutNeeded = true; + } + pos = newPos; + } + } + } + + if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/)) { + assignLayersLocked(displayContent.getWindowList()); + } + + mInputMonitor.setUpdateInputWindowsNeededLw(); + performLayoutAndPlaceSurfacesLocked(); + mInputMonitor.updateInputWindowsLw(false /*force*/); + + //dump(); + } + + public void moveTaskToTop(int taskId) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + // Normal behavior, addAppToken will be called next and task will be created. + return; + } + final TaskStack stack = task.mStack; + final DisplayContent displayContent = task.getDisplayContent(); + displayContent.moveStack(stack, true); + final TaskStack homeStack = displayContent.getHomeStack(); + if (homeStack != stack) { + // When a non-home stack moves to the top, the home stack moves to the + // bottom. + displayContent.moveStack(homeStack, false); + } + stack.moveTaskToTop(task); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + public void moveTaskToBottom(int taskId) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId + + " not found in mTaskIdToTask"); + return; + } + final TaskStack stack = task.mStack; + stack.moveTaskToBottom(task); + moveStackWindowsLocked(stack.getDisplayContent()); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Create a new TaskStack and place it next to an existing stack. + * @param stackId The unique identifier of the new stack. + */ + public void createStack(int stackId, int displayId) { + synchronized (mWindowMap) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + if (displayContent.getDisplayId() == displayId) { + TaskStack stack = displayContent.createStack(stackId); + mStackIdToStack.put(stackId, stack); + performLayoutAndPlaceSurfacesLocked(); + return; + } + } + } + } + + public int removeStack(int stackId) { + synchronized (mWindowMap) { + final TaskStack stack = mStackIdToStack.get(stackId); + if (stack != null) { + mStackIdToStack.delete(stackId); + int nextStackId = stack.remove(); + stack.getDisplayContent().layoutNeeded = true; + requestTraversalLocked(); + if (nextStackId > HOME_STACK_ID) { + return nextStackId; + } + } + if (DEBUG_STACK) Slog.i(TAG, "removeStack: could not find stackId=" + stackId); + } + return HOME_STACK_ID; + } + + public void removeTask(int taskId) { + synchronized (mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + if (DEBUG_STACK) Slog.i(TAG, "removeTask: could not find taskId=" + taskId); + return; + } + final TaskStack stack = task.mStack; + EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, taskId, "removeTask"); + stack.removeTask(task); + stack.getDisplayContent().layoutNeeded = true; + } + } + + public void addTask(int taskId, int stackId, boolean toTop) { + synchronized (mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + return; + } + TaskStack stack = mStackIdToStack.get(stackId); + stack.addTask(task, toTop); + final DisplayContent displayContent = stack.getDisplayContent(); + displayContent.layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } + + public void resizeStack(int stackId, Rect bounds) { + synchronized (mWindowMap) { + final TaskStack stack = mStackIdToStack.get(stackId); + if (stack == null) { + throw new IllegalArgumentException("resizeStack: stackId " + stackId + + " not found."); + } + if (stack.setBounds(bounds)) { + stack.getDisplayContent().layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } + } + + public void getStackBounds(int stackId, Rect bounds) { + final TaskStack stack = mStackIdToStack.get(stackId); + if (stack != null) { + stack.getBounds(bounds); + return; + } + bounds.setEmpty(); + } + + // ------------------------------------------------------------- + // Misc IWindowSession methods + // ------------------------------------------------------------- + + @Override + public void startFreezingScreen(int exitAnim, int enterAnim) { + if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, + "startFreezingScreen()")) { + throw new SecurityException("Requires FREEZE_SCREEN permission"); + } + + synchronized(mWindowMap) { + if (!mClientFreezingScreen) { + mClientFreezingScreen = true; + final long origId = Binder.clearCallingIdentity(); + try { + startFreezingDisplayLocked(false, exitAnim, enterAnim); + mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); + mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + } + + @Override + public void stopFreezingScreen() { + if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, + "stopFreezingScreen()")) { + throw new SecurityException("Requires FREEZE_SCREEN permission"); + } + + synchronized(mWindowMap) { + if (mClientFreezingScreen) { + mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client"; + final long origId = Binder.clearCallingIdentity(); + try { + stopFreezingDisplayLocked(); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + } + + @Override + public void disableKeyguard(IBinder token, String tag) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + + mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( + KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag))); + } + + @Override + public void reenableKeyguard(IBinder token) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + + mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( + KeyguardDisableHandler.KEYGUARD_REENABLE, token)); + } + + /** + * @see android.app.KeyguardManager#exitKeyguardSecurely + */ + @Override + public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() { + @Override + public void onKeyguardExitResult(boolean success) { + try { + callback.onKeyguardExitResult(success); + } catch (RemoteException e) { + // Client has died, we don't care. + } + } + }); + } + + @Override + public boolean inKeyguardRestrictedInputMode() { + return mPolicy.inKeyguardRestrictedKeyInputMode(); + } + + @Override + public boolean isKeyguardLocked() { + return mPolicy.isKeyguardLocked(); + } + + @Override + public boolean isKeyguardSecure() { + return mPolicy.isKeyguardSecure(); + } + + @Override + public void dismissKeyguard() { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + synchronized(mWindowMap) { + mPolicy.dismissKeyguardLw(); + } + } + + @Override + public void closeSystemDialogs(String reason) { + synchronized(mWindowMap) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState w = windows.get(winNdx); + if (w.mHasSurface) { + try { + w.mClient.closeSystemDialogs(reason); + } catch (RemoteException e) { + } + } + } + } + } + } + + static float fixScale(float scale) { + if (scale < 0) scale = 0; + else if (scale > 20) scale = 20; + return Math.abs(scale); + } + + @Override + public void setAnimationScale(int which, float scale) { + if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, + "setAnimationScale()")) { + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); + } + + scale = fixScale(scale); + switch (which) { + case 0: mWindowAnimationScale = scale; break; + case 1: mTransitionAnimationScale = scale; break; + case 2: mAnimatorDurationScale = scale; break; + } + + // Persist setting + mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); + } + + @Override + public void setAnimationScales(float[] scales) { + if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, + "setAnimationScale()")) { + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); + } + + if (scales != null) { + if (scales.length >= 1) { + mWindowAnimationScale = fixScale(scales[0]); + } + if (scales.length >= 2) { + mTransitionAnimationScale = fixScale(scales[1]); + } + if (scales.length >= 3) { + setAnimatorDurationScale(fixScale(scales[2])); + } + } + + // Persist setting + mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); + } + + private void setAnimatorDurationScale(float scale) { + mAnimatorDurationScale = scale; + ValueAnimator.setDurationScale(scale); + } + + @Override + public float getAnimationScale(int which) { + switch (which) { + case 0: return mWindowAnimationScale; + case 1: return mTransitionAnimationScale; + case 2: return mAnimatorDurationScale; + } + return 0; + } + + @Override + public float[] getAnimationScales() { + return new float[] { mWindowAnimationScale, mTransitionAnimationScale, + mAnimatorDurationScale }; + } + + @Override + public void registerPointerEventListener(PointerEventListener listener) { + mPointerEventDispatcher.registerInputEventListener(listener); + } + + @Override + public void unregisterPointerEventListener(PointerEventListener listener) { + mPointerEventDispatcher.unregisterInputEventListener(listener); + } + + // Called by window manager policy. Not exposed externally. + @Override + public int getLidState() { + int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, + InputManagerService.SW_LID); + if (sw > 0) { + // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL. + return LID_CLOSED; + } else if (sw == 0) { + // Switch state: AKEY_STATE_UP. + return LID_OPEN; + } else { + // Switch state: AKEY_STATE_UNKNOWN. + return LID_ABSENT; + } + } + + // Called by window manager policy. Not exposed externally. + @Override + public void switchKeyboardLayout(int deviceId, int direction) { + mInputManager.switchKeyboardLayout(deviceId, direction); + } + + // Called by window manager policy. Not exposed externally. + @Override + public void shutdown(boolean confirm) { + ShutdownThread.shutdown(mContext, confirm); + } + + // Called by window manager policy. Not exposed externally. + @Override + public void rebootSafeMode(boolean confirm) { + ShutdownThread.rebootSafeMode(mContext, confirm); + } + + @Override + public void setInputFilter(IInputFilter filter) { + if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) { + throw new SecurityException("Requires FILTER_EVENTS permission"); + } + mInputManager.setInputFilter(filter); + } + + @Override + public void setTouchExplorationEnabled(boolean enabled) { + mPolicy.setTouchExplorationEnabled(enabled); + } + + public void setCurrentUser(final int newUserId) { + synchronized (mWindowMap) { + int oldUserId = mCurrentUserId; + mCurrentUserId = newUserId; + mAppTransition.setCurrentUser(newUserId); + mPolicy.setCurrentUserLw(newUserId); + + // Hide windows that should not be seen by the new user. + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + displayContent.switchUserStacks(oldUserId, newUserId); + rebuildAppWindowListLocked(displayContent); + } + performLayoutAndPlaceSurfacesLocked(); + } + } + + public void enableScreenAfterBoot() { + synchronized(mWindowMap) { + if (DEBUG_BOOT) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled + + " mForceDisplayEnabled=" + mForceDisplayEnabled + + " mShowingBootMessages=" + mShowingBootMessages + + " mSystemBooted=" + mSystemBooted, here); + } + if (mSystemBooted) { + return; + } + mSystemBooted = true; + hideBootMessagesLocked(); + // If the screen still doesn't come up after 30 seconds, give + // up and turn it on. + mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000); + } + + mPolicy.systemBooted(); + + performEnableScreen(); + } + + void enableScreenIfNeededLocked() { + if (DEBUG_BOOT) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "enableScreenIfNeededLocked: mDisplayEnabled=" + mDisplayEnabled + + " mForceDisplayEnabled=" + mForceDisplayEnabled + + " mShowingBootMessages=" + mShowingBootMessages + + " mSystemBooted=" + mSystemBooted, here); + } + if (mDisplayEnabled) { + return; + } + if (!mSystemBooted && !mShowingBootMessages) { + return; + } + mH.sendEmptyMessage(H.ENABLE_SCREEN); + } + + public void performBootTimeout() { + synchronized(mWindowMap) { + if (mDisplayEnabled) { + return; + } + Slog.w(TAG, "***** BOOT TIMEOUT: forcing display enabled"); + mForceDisplayEnabled = true; + } + performEnableScreen(); + } + + public void performEnableScreen() { + synchronized(mWindowMap) { + if (DEBUG_BOOT) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled + + " mForceDisplayEnabled=" + mForceDisplayEnabled + + " mShowingBootMessages=" + mShowingBootMessages + + " mSystemBooted=" + mSystemBooted + + " mOnlyCore=" + mOnlyCore, here); + } + if (mDisplayEnabled) { + return; + } + if (!mSystemBooted && !mShowingBootMessages) { + return; + } + + if (!mForceDisplayEnabled) { + // Don't enable the screen until all existing windows + // have been drawn. + boolean haveBootMsg = false; + boolean haveApp = false; + // if the wallpaper service is disabled on the device, we're never going to have + // wallpaper, don't bother waiting for it + boolean haveWallpaper = false; + boolean wallpaperEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableWallpaperService) + && !mOnlyCore; + boolean haveKeyguard = true; + // TODO(multidisplay): Expand to all displays? + final WindowList windows = getDefaultWindowListLocked(); + final int N = windows.size(); + for (int i=0; i<N; i++) { + WindowState w = windows.get(i); + if (w.mAttrs.type == TYPE_KEYGUARD) { + // Only if there is a keyguard attached to the window manager + // will we consider ourselves as having a keyguard. If it + // isn't attached, we don't know if it wants to be shown or + // hidden. If it is attached, we will say we have a keyguard + // if the window doesn't want to be visible, because in that + // case it explicitly doesn't want to be shown so we should + // not delay turning the screen on for it. + boolean vis = w.mViewVisibility == View.VISIBLE + && w.mPolicyVisibility; + haveKeyguard = !vis; + } + if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) { + return; + } + if (w.isDrawnLw()) { + if (w.mAttrs.type == TYPE_BOOT_PROGRESS) { + haveBootMsg = true; + } else if (w.mAttrs.type == TYPE_APPLICATION) { + haveApp = true; + } else if (w.mAttrs.type == TYPE_WALLPAPER) { + haveWallpaper = true; + } else if (w.mAttrs.type == TYPE_KEYGUARD) { + haveKeyguard = true; + } + } + } + + if (DEBUG_SCREEN_ON || DEBUG_BOOT) { + Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages + + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp + + " haveWall=" + haveWallpaper + " wallEnabled=" + wallpaperEnabled + + " haveKeyguard=" + haveKeyguard); + } + + // If we are turning on the screen to show the boot message, + // don't do it until the boot message is actually displayed. + if (!mSystemBooted && !haveBootMsg) { + return; + } + + // If we are turning on the screen after the boot is completed + // normally, don't do so until we have the application and + // wallpaper. + if (mSystemBooted && ((!haveApp && !haveKeyguard) || + (wallpaperEnabled && !haveWallpaper))) { + return; + } + } + + mDisplayEnabled = true; + if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG, "******************** ENABLING SCREEN!"); + if (false) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 1024); + this.dump(null, pw, null); + pw.flush(); + Slog.i(TAG, sw.toString()); + } + try { + IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); + if (surfaceFlinger != null) { + //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!"); + Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED + data, null, 0); + data.recycle(); + } + } catch (RemoteException ex) { + Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!"); + } + + // Enable input dispatch. + mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled); + } + + mPolicy.enableScreenAfterBoot(); + + // Make sure the last requested orientation has been applied. + updateRotationUnchecked(false, false); + } + + public void showBootMessage(final CharSequence msg, final boolean always) { + boolean first = false; + synchronized(mWindowMap) { + if (DEBUG_BOOT) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "showBootMessage: msg=" + msg + " always=" + always + + " mAllowBootMessages=" + mAllowBootMessages + + " mShowingBootMessages=" + mShowingBootMessages + + " mSystemBooted=" + mSystemBooted, here); + } + if (!mAllowBootMessages) { + return; + } + if (!mShowingBootMessages) { + if (!always) { + return; + } + first = true; + } + if (mSystemBooted) { + return; + } + mShowingBootMessages = true; + mPolicy.showBootMessage(msg, always); + } + if (first) { + performEnableScreen(); + } + } + + public void hideBootMessagesLocked() { + if (DEBUG_BOOT) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "hideBootMessagesLocked: mDisplayEnabled=" + mDisplayEnabled + + " mForceDisplayEnabled=" + mForceDisplayEnabled + + " mShowingBootMessages=" + mShowingBootMessages + + " mSystemBooted=" + mSystemBooted, here); + } + if (mShowingBootMessages) { + mShowingBootMessages = false; + mPolicy.hideBootMessages(); + } + } + + @Override + public void setInTouchMode(boolean mode) { + synchronized(mWindowMap) { + mInTouchMode = mode; + } + } + + // TODO: more accounting of which pid(s) turned it on, keep count, + // only allow disables from pids which have count on, etc. + @Override + public void showStrictModeViolation(boolean on) { + int pid = Binder.getCallingPid(); + mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, pid)); + } + + private void showStrictModeViolation(int arg, int pid) { + final boolean on = arg != 0; + synchronized(mWindowMap) { + // Ignoring requests to enable the red border from clients + // which aren't on screen. (e.g. Broadcast Receivers in + // the background..) + if (on) { + boolean isVisible = false; + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState ws = windows.get(winNdx); + if (ws.mSession.mPid == pid && ws.isVisibleLw()) { + isVisible = true; + break; + } + } + } + if (!isVisible) { + return; + } + } + + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + ">>> OPEN TRANSACTION showStrictModeViolation"); + SurfaceControl.openTransaction(); + try { + // TODO(multi-display): support multiple displays + if (mStrictModeFlash == null) { + mStrictModeFlash = new StrictModeFlash( + getDefaultDisplayContentLocked().getDisplay(), mFxSession); + } + mStrictModeFlash.setVisibility(on); + } finally { + SurfaceControl.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + "<<< CLOSE TRANSACTION showStrictModeViolation"); + } + } + } + + @Override + public void setStrictModeVisualIndicatorPreference(String value) { + SystemProperties.set(StrictMode.VISUAL_PROPERTY, value); + } + + /** + * Takes a snapshot of the screen. In landscape mode this grabs the whole screen. + * In portrait mode, it grabs the upper region of the screen based on the vertical dimension + * of the target image. + * + * @param displayId the Display to take a screenshot of. + * @param width the width of the target bitmap + * @param height the height of the target bitmap + * @param force565 if true the returned bitmap will be RGB_565, otherwise it + * will be the same config as the surface + */ + @Override + public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, + int height, boolean force565) { + if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, + "screenshotApplications()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + + Bitmap rawss = null; + + int maxLayer = 0; + final Rect frame = new Rect(); + + float scale = 0; + int dw, dh; + int rot = Surface.ROTATION_0; + + boolean screenshotReady; + int minLayer; + if (appToken == null) { + screenshotReady = true; + minLayer = 0; + } else { + screenshotReady = false; + minLayer = Integer.MAX_VALUE; + } + + int retryCount = 0; + WindowState appWin = null; + + do { + if (retryCount++ > 0) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent == null) { + return null; + } + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + dw = displayInfo.logicalWidth; + dh = displayInfo.logicalHeight; + + int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; + aboveAppLayer += TYPE_LAYER_MULTIPLIER; + + boolean isImeTarget = mInputMethodTarget != null + && mInputMethodTarget.mAppToken != null + && mInputMethodTarget.mAppToken.appToken != null + && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken; + + // Figure out the part of the screen that is actually the app. + boolean including = false; + appWin = null; + final WindowList windows = displayContent.getWindowList(); + final Rect stackBounds = new Rect(); + for (int i = windows.size() - 1; i >= 0; i--) { + WindowState ws = windows.get(i); + if (!ws.mHasSurface) { + continue; + } + if (ws.mLayer >= aboveAppLayer) { + continue; + } + // When we will skip windows: when we are not including + // ones behind a window we didn't skip, and we are actually + // taking a screenshot of a specific app. + if (!including && appToken != null) { + // Also, we can possibly skip this window if it is not + // an IME target or the application for the screenshot + // is not the current IME target. + if (!ws.mIsImWindow || !isImeTarget) { + // And finally, this window is of no interest if it + // is not associated with the screenshot app. + if (ws.mAppToken == null || ws.mAppToken.token != appToken) { + continue; + } + appWin = ws; + ws.getStackBounds(stackBounds); + } + } + + // We keep on including windows until we go past a full-screen + // window. + including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh); + + final WindowStateAnimator winAnim = ws.mWinAnimator; + if (maxLayer < winAnim.mSurfaceLayer) { + maxLayer = winAnim.mSurfaceLayer; + } + if (minLayer > winAnim.mSurfaceLayer) { + minLayer = winAnim.mSurfaceLayer; + } + + // Don't include wallpaper in bounds calculation + if (!ws.mIsWallpaper) { + final Rect wf = ws.mFrame; + final Rect cr = ws.mContentInsets; + int left = wf.left + cr.left; + int top = wf.top + cr.top; + int right = wf.right - cr.right; + int bottom = wf.bottom - cr.bottom; + frame.union(left, top, right, bottom); + frame.intersect(stackBounds); + } + + if (ws.mAppToken != null && ws.mAppToken.token == appToken && + ws.isDisplayedLw()) { + screenshotReady = true; + } + } + + if (appToken != null && appWin == null) { + // Can't find a window to snapshot. + if (DEBUG_SCREENSHOT) Slog.i(TAG, + "Screenshot: Couldn't find a surface matching " + appToken); + return null; + } + if (!screenshotReady) { + // Delay and hope that window gets drawn. + if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken + + ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState); + continue; + } + + // Constrain frame to the screen size. + frame.intersect(0, 0, dw, dh); + + if (frame.isEmpty() || maxLayer == 0) { + if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken + + ": returning null frame=" + frame.toShortString() + " maxLayer=" + + maxLayer); + return null; + } + + // The screenshot API does not apply the current screen rotation. + rot = getDefaultDisplayContentLocked().getDisplay().getRotation(); + int fw = frame.width(); + int fh = frame.height(); + + // Constrain thumbnail to smaller of screen width or height. Assumes aspect + // of thumbnail is the same as the screen (in landscape) or square. + scale = Math.max(width / (float) fw, height / (float) fh); + /* + float targetWidthScale = width / (float) fw; + float targetHeightScale = height / (float) fh; + if (fw <= fh) { + scale = targetWidthScale; + // If aspect of thumbnail is the same as the screen (in landscape), + // select the slightly larger value so we fill the entire bitmap + if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) { + scale = targetHeightScale; + } + } else { + scale = targetHeightScale; + // If aspect of thumbnail is the same as the screen (in landscape), + // select the slightly larger value so we fill the entire bitmap + if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) { + scale = targetWidthScale; + } + } + */ + + // The screen shot will contain the entire screen. + dw = (int)(dw*scale); + dh = (int)(dh*scale); + if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { + int tmp = dw; + dw = dh; + dh = tmp; + rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90; + } + if (DEBUG_SCREENSHOT) { + Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to " + + maxLayer + " appToken=" + appToken); + for (int i = 0; i < windows.size(); i++) { + WindowState win = windows.get(i); + Slog.i(TAG, win + ": " + win.mLayer + + " animLayer=" + win.mWinAnimator.mAnimLayer + + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer); + } + } + rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer); + } + } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES); + if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " + + retryCount + " of " + appToken + " appWin=" + (appWin == null ? + "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState))); + + if (rawss == null) { + Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh + + ") to layer " + maxLayer); + return null; + } + + Bitmap bm = Bitmap.createBitmap(width, height, force565 ? Config.RGB_565 : rawss.getConfig()); + frame.scale(scale); + Matrix matrix = new Matrix(); + ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix); + // TODO: Test for RTL vs. LTR and use frame.right-width instead of -frame.left + matrix.postTranslate(-FloatMath.ceil(frame.left), -FloatMath.ceil(frame.top)); + Canvas canvas = new Canvas(bm); + canvas.drawColor(0xFF000000); + canvas.drawBitmap(rawss, matrix, null); + canvas.setBitmap(null); + + if (DEBUG_SCREENSHOT) { + // TEST IF IT's ALL BLACK + int[] buffer = new int[bm.getWidth() * bm.getHeight()]; + bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight()); + boolean allBlack = true; + final int firstColor = buffer[0]; + for (int i = 0; i < buffer.length; i++) { + if (buffer[i] != firstColor) { + allBlack = false; + break; + } + } + if (allBlack) { + Slog.i(TAG, "Screenshot " + appWin + " was monochrome(" + + Integer.toHexString(firstColor) + ")! mSurfaceLayer=" + + (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") + + " minLayer=" + minLayer + " maxLayer=" + maxLayer); + } + } + + rawss.recycle(); + return bm; + } + + /** + * Freeze rotation changes. (Enable "rotation lock".) + * Persists across reboots. + * @param rotation The desired rotation to freeze to, or -1 to use the + * current rotation. + */ + @Override + public void freezeRotation(int rotation) { + if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, + "freezeRotation()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } + if (rotation < -1 || rotation > Surface.ROTATION_270) { + throw new IllegalArgumentException("Rotation argument must be -1 or a valid " + + "rotation constant."); + } + + if (DEBUG_ORIENTATION) Slog.v(TAG, "freezeRotation: mRotation=" + mRotation); + + long origId = Binder.clearCallingIdentity(); + try { + mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED, + rotation == -1 ? mRotation : rotation); + } finally { + Binder.restoreCallingIdentity(origId); + } + + updateRotationUnchecked(false, false); + } + + /** + * Thaw rotation changes. (Disable "rotation lock".) + * Persists across reboots. + */ + @Override + public void thawRotation() { + if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, + "thawRotation()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } + + if (DEBUG_ORIENTATION) Slog.v(TAG, "thawRotation: mRotation=" + mRotation); + + long origId = Binder.clearCallingIdentity(); + try { + mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, + 777); // rot not used + } finally { + Binder.restoreCallingIdentity(origId); + } + + updateRotationUnchecked(false, false); + } + + /** + * Recalculate the current rotation. + * + * Called by the window manager policy whenever the state of the system changes + * such that the current rotation might need to be updated, such as when the + * device is docked or rotated into a new posture. + */ + @Override + public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { + updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); + } + + /** + * Temporarily pauses rotation changes until resumed. + * + * This can be used to prevent rotation changes from occurring while the user is + * performing certain operations, such as drag and drop. + * + * This call nests and must be matched by an equal number of calls to + * {@link #resumeRotationLocked}. + */ + void pauseRotationLocked() { + mDeferredRotationPauseCount += 1; + } + + /** + * Resumes normal rotation changes after being paused. + */ + void resumeRotationLocked() { + if (mDeferredRotationPauseCount > 0) { + mDeferredRotationPauseCount -= 1; + if (mDeferredRotationPauseCount == 0) { + boolean changed = updateRotationUncheckedLocked(false); + if (changed) { + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + } + } + + public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { + if(DEBUG_ORIENTATION) Slog.v(TAG, "updateRotationUnchecked(" + + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")"); + + long origId = Binder.clearCallingIdentity(); + boolean changed; + synchronized(mWindowMap) { + changed = updateRotationUncheckedLocked(false); + if (!changed || forceRelayout) { + getDefaultDisplayContentLocked().layoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } + + if (changed || alwaysSendConfiguration) { + sendNewConfiguration(); + } + + Binder.restoreCallingIdentity(origId); + } + + // TODO(multidisplay): Rotate any display? + /** + * Updates the current rotation. + * + * Returns true if the rotation has been changed. In this case YOU + * MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN. + */ + public boolean updateRotationUncheckedLocked(boolean inTransaction) { + if (mDeferredRotationPauseCount > 0) { + // Rotation updates have been paused temporarily. Defer the update until + // updates have been resumed. + if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, rotation is paused."); + return false; + } + + ScreenRotationAnimation screenRotationAnimation = + mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); + if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { + // Rotation updates cannot be performed while the previous rotation change + // animation is still in progress. Skip this update. We will try updating + // again after the animation is finished and the display is unfrozen. + if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, animation in progress."); + return false; + } + + if (!mDisplayEnabled) { + // No point choosing a rotation if the display is not enabled. + if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, display is not enabled."); + return false; + } + + // TODO: Implement forced rotation changes. + // Set mAltOrientation to indicate that the application is receiving + // an orientation that has different metrics than it expected. + // eg. Portrait instead of Landscape. + + int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation); + boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( + mForcedAppOrientation, rotation); + + if (DEBUG_ORIENTATION) { + Slog.v(TAG, "Application requested orientation " + + mForcedAppOrientation + ", got rotation " + rotation + + " which has " + (altOrientation ? "incompatible" : "compatible") + + " metrics"); + } + + if (mRotation == rotation && mAltOrientation == altOrientation) { + // No change. + return false; + } + + if (DEBUG_ORIENTATION) { + Slog.v(TAG, + "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "") + + " from " + mRotation + (mAltOrientation ? " (alt)" : "") + + ", forceApp=" + mForcedAppOrientation); + } + + mRotation = rotation; + mAltOrientation = altOrientation; + mPolicy.setRotationLw(mRotation); + + mWindowsFreezingScreen = true; + mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); + mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION); + mWaitingForConfig = true; + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + displayContent.layoutNeeded = true; + final int[] anim = new int[2]; + if (displayContent.isDimming()) { + anim[0] = anim[1] = 0; + } else { + mPolicy.selectRotationAnimationLw(anim); + } + startFreezingDisplayLocked(inTransaction, anim[0], anim[1]); + // startFreezingDisplayLocked can reset the ScreenRotationAnimation. + screenRotationAnimation = + mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); + + // We need to update our screen size information to match the new + // rotation. Note that this is redundant with the later call to + // sendNewConfiguration() that must be called after this function + // returns... however we need to do the screen size part of that + // before then so we have the correct size to use when initializing + // the rotation animation for the new rotation. + computeScreenConfigurationLocked(null); + + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + if (!inTransaction) { + if (SHOW_TRANSACTIONS) { + Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked"); + } + SurfaceControl.openTransaction(); + } + try { + // NOTE: We disable the rotation in the emulator because + // it doesn't support hardware OpenGL emulation yet. + if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null + && screenRotationAnimation.hasScreenshot()) { + if (screenRotationAnimation.setRotationInTransaction( + rotation, mFxSession, + MAX_ANIMATION_DURATION, mTransitionAnimationScale, + displayInfo.logicalWidth, displayInfo.logicalHeight)) { + scheduleAnimationLocked(); + } + } + + mDisplayManagerService.performTraversalInTransactionFromWindowManager(); + } finally { + if (!inTransaction) { + SurfaceControl.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked"); + } + } + } + + final WindowList windows = displayContent.getWindowList(); + for (int i = windows.size() - 1; i >= 0; i--) { + WindowState w = windows.get(i); + if (w.mHasSurface) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w); + w.mOrientationChanging = true; + mInnerFields.mOrientationChangeComplete = false; + } + w.mLastFreezeDuration = 0; + } + + for (int i=mRotationWatchers.size()-1; i>=0; i--) { + try { + mRotationWatchers.get(i).onRotationChanged(rotation); + } catch (RemoteException e) { + } + } + + //TODO (multidisplay): Magnification is supported only for the default display. + if (mDisplayMagnifier != null + && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation); + } + + return true; + } + + @Override + public int getRotation() { + return mRotation; + } + + @Override + public boolean isRotationFrozen() { + return mPolicy.getUserRotationMode() == WindowManagerPolicy.USER_ROTATION_LOCKED; + } + + @Override + public int watchRotation(IRotationWatcher watcher) { + final IBinder watcherBinder = watcher.asBinder(); + IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mWindowMap) { + for (int i=0; i<mRotationWatchers.size(); i++) { + if (watcherBinder == mRotationWatchers.get(i).asBinder()) { + IRotationWatcher removed = mRotationWatchers.remove(i); + if (removed != null) { + removed.asBinder().unlinkToDeath(this, 0); + } + i--; + } + } + } + } + }; + + synchronized (mWindowMap) { + try { + watcher.asBinder().linkToDeath(dr, 0); + mRotationWatchers.add(watcher); + } catch (RemoteException e) { + // Client died, no cleanup needed. + } + + return mRotation; + } + } + + @Override + public void removeRotationWatcher(IRotationWatcher watcher) { + final IBinder watcherBinder = watcher.asBinder(); + synchronized (mWindowMap) { + for (int i=0; i<mRotationWatchers.size(); i++) { + if (watcherBinder == mRotationWatchers.get(i).asBinder()) { + mRotationWatchers.remove(i); + i--; + } + } + } + } + + /** + * Apps that use the compact menu panel (as controlled by the panelMenuIsCompact + * theme attribute) on devices that feature a physical options menu key attempt to position + * their menu panel window along the edge of the screen nearest the physical menu key. + * This lowers the travel distance between invoking the menu panel and selecting + * a menu option. + * + * This method helps control where that menu is placed. Its current implementation makes + * assumptions about the menu key and its relationship to the screen based on whether + * the device's natural orientation is portrait (width < height) or landscape. + * + * The menu key is assumed to be located along the bottom edge of natural-portrait + * devices and along the right edge of natural-landscape devices. If these assumptions + * do not hold for the target device, this method should be changed to reflect that. + * + * @return A {@link Gravity} value for placing the options menu window + */ + @Override + public int getPreferredOptionsPanelGravity() { + synchronized (mWindowMap) { + final int rotation = getRotation(); + + // TODO(multidisplay): Assume that such devices physical keys are on the main screen. + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) { + // On devices with a natural orientation of portrait + switch (rotation) { + default: + case Surface.ROTATION_0: + return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; + case Surface.ROTATION_90: + return Gravity.RIGHT | Gravity.BOTTOM; + case Surface.ROTATION_180: + return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; + case Surface.ROTATION_270: + return Gravity.START | Gravity.BOTTOM; + } + } + + // On devices with a natural orientation of landscape + switch (rotation) { + default: + case Surface.ROTATION_0: + return Gravity.RIGHT | Gravity.BOTTOM; + case Surface.ROTATION_90: + return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; + case Surface.ROTATION_180: + return Gravity.START | Gravity.BOTTOM; + case Surface.ROTATION_270: + return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; + } + } + } + + /** + * Starts the view server on the specified port. + * + * @param port The port to listener to. + * + * @return True if the server was successfully started, false otherwise. + * + * @see com.android.server.wm.ViewServer + * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT + */ + @Override + public boolean startViewServer(int port) { + if (isSystemSecure()) { + return false; + } + + if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) { + return false; + } + + if (port < 1024) { + return false; + } + + if (mViewServer != null) { + if (!mViewServer.isRunning()) { + try { + return mViewServer.start(); + } catch (IOException e) { + Slog.w(TAG, "View server did not start"); + } + } + return false; + } + + try { + mViewServer = new ViewServer(this, port); + return mViewServer.start(); + } catch (IOException e) { + Slog.w(TAG, "View server did not start"); + } + return false; + } + + private boolean isSystemSecure() { + return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) && + "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + } + + /** + * Stops the view server if it exists. + * + * @return True if the server stopped, false if it wasn't started or + * couldn't be stopped. + * + * @see com.android.server.wm.ViewServer + */ + @Override + public boolean stopViewServer() { + if (isSystemSecure()) { + return false; + } + + if (!checkCallingPermission(Manifest.permission.DUMP, "stopViewServer")) { + return false; + } + + if (mViewServer != null) { + return mViewServer.stop(); + } + return false; + } + + /** + * Indicates whether the view server is running. + * + * @return True if the server is running, false otherwise. + * + * @see com.android.server.wm.ViewServer + */ + @Override + public boolean isViewServerRunning() { + if (isSystemSecure()) { + return false; + } + + if (!checkCallingPermission(Manifest.permission.DUMP, "isViewServerRunning")) { + return false; + } + + return mViewServer != null && mViewServer.isRunning(); + } + + /** + * Lists all availble windows in the system. The listing is written in the + * specified Socket's output stream with the following syntax: + * windowHashCodeInHexadecimal windowName + * Each line of the ouput represents a different window. + * + * @param client The remote client to send the listing to. + * @return False if an error occured, true otherwise. + */ + boolean viewServerListWindows(Socket client) { + if (isSystemSecure()) { + return false; + } + + boolean result = true; + + WindowList windows = new WindowList(); + synchronized (mWindowMap) { + //noinspection unchecked + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + windows.addAll(displayContent.getWindowList()); + } + } + + BufferedWriter out = null; + + // Any uncaught exception will crash the system process + try { + OutputStream clientStream = client.getOutputStream(); + out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); + + final int count = windows.size(); + for (int i = 0; i < count; i++) { + final WindowState w = windows.get(i); + out.write(Integer.toHexString(System.identityHashCode(w))); + out.write(' '); + out.append(w.mAttrs.getTitle()); + out.write('\n'); + } + + out.write("DONE.\n"); + out.flush(); + } catch (Exception e) { + result = false; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + result = false; + } + } + } + + return result; + } + + // TODO(multidisplay): Extend to multiple displays. + /** + * Returns the focused window in the following format: + * windowHashCodeInHexadecimal windowName + * + * @param client The remote client to send the listing to. + * @return False if an error occurred, true otherwise. + */ + boolean viewServerGetFocusedWindow(Socket client) { + if (isSystemSecure()) { + return false; + } + + boolean result = true; + + WindowState focusedWindow = getFocusedWindow(); + + BufferedWriter out = null; + + // Any uncaught exception will crash the system process + try { + OutputStream clientStream = client.getOutputStream(); + out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); + + if(focusedWindow != null) { + out.write(Integer.toHexString(System.identityHashCode(focusedWindow))); + out.write(' '); + out.append(focusedWindow.mAttrs.getTitle()); + } + out.write('\n'); + out.flush(); + } catch (Exception e) { + result = false; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + result = false; + } + } + } + + return result; + } + + /** + * Sends a command to a target window. The result of the command, if any, will be + * written in the output stream of the specified socket. + * + * The parameters must follow this syntax: + * windowHashcode extra + * + * Where XX is the length in characeters of the windowTitle. + * + * The first parameter is the target window. The window with the specified hashcode + * will be the target. If no target can be found, nothing happens. The extra parameters + * will be delivered to the target window and as parameters to the command itself. + * + * @param client The remote client to sent the result, if any, to. + * @param command The command to execute. + * @param parameters The command parameters. + * + * @return True if the command was successfully delivered, false otherwise. This does + * not indicate whether the command itself was successful. + */ + boolean viewServerWindowCommand(Socket client, String command, String parameters) { + if (isSystemSecure()) { + return false; + } + + boolean success = true; + Parcel data = null; + Parcel reply = null; + + BufferedWriter out = null; + + // Any uncaught exception will crash the system process + try { + // Find the hashcode of the window + int index = parameters.indexOf(' '); + if (index == -1) { + index = parameters.length(); + } + final String code = parameters.substring(0, index); + int hashCode = (int) Long.parseLong(code, 16); + + // Extract the command's parameter after the window description + if (index < parameters.length()) { + parameters = parameters.substring(index + 1); + } else { + parameters = ""; + } + + final WindowState window = findWindow(hashCode); + if (window == null) { + return false; + } + + data = Parcel.obtain(); + data.writeInterfaceToken("android.view.IWindow"); + data.writeString(command); + data.writeString(parameters); + data.writeInt(1); + ParcelFileDescriptor.fromSocket(client).writeToParcel(data, 0); + + reply = Parcel.obtain(); + + final IBinder binder = window.mClient.asBinder(); + // TODO: GET THE TRANSACTION CODE IN A SAFER MANNER + binder.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + + reply.readException(); + + if (!client.isOutputShutdown()) { + out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); + out.write("DONE\n"); + out.flush(); + } + + } catch (Exception e) { + Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e); + success = false; + } finally { + if (data != null) { + data.recycle(); + } + if (reply != null) { + reply.recycle(); + } + if (out != null) { + try { + out.close(); + } catch (IOException e) { + + } + } + } + + return success; + } + + public void addWindowChangeListener(WindowChangeListener listener) { + synchronized(mWindowMap) { + mWindowChangeListeners.add(listener); + } + } + + public void removeWindowChangeListener(WindowChangeListener listener) { + synchronized(mWindowMap) { + mWindowChangeListeners.remove(listener); + } + } + + private void notifyWindowsChanged() { + WindowChangeListener[] windowChangeListeners; + synchronized(mWindowMap) { + if(mWindowChangeListeners.isEmpty()) { + return; + } + windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()]; + windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners); + } + int N = windowChangeListeners.length; + for(int i = 0; i < N; i++) { + windowChangeListeners[i].windowsChanged(); + } + } + + private void notifyFocusChanged() { + WindowChangeListener[] windowChangeListeners; + synchronized(mWindowMap) { + if(mWindowChangeListeners.isEmpty()) { + return; + } + windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()]; + windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners); + } + int N = windowChangeListeners.length; + for(int i = 0; i < N; i++) { + windowChangeListeners[i].focusChanged(); + } + } + + private WindowState findWindow(int hashCode) { + if (hashCode == -1) { + // TODO(multidisplay): Extend to multiple displays. + return getFocusedWindow(); + } + + synchronized (mWindowMap) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState w = windows.get(winNdx); + if (System.identityHashCode(w) == hashCode) { + return w; + } + } + } + } + + return null; + } + + /* + * Instruct the Activity Manager to fetch the current configuration and broadcast + * that to config-changed listeners if appropriate. + */ + void sendNewConfiguration() { + try { + mActivityManager.updateConfiguration(null); + } catch (RemoteException e) { + } + } + + public Configuration computeNewConfiguration() { + synchronized (mWindowMap) { + Configuration config = computeNewConfigurationLocked(); + if (config == null && mWaitingForConfig) { + // Nothing changed but we are waiting for something... stop that! + mWaitingForConfig = false; + mLastFinishedFreezeSource = "new-config"; + performLayoutAndPlaceSurfacesLocked(); + } + return config; + } + } + + Configuration computeNewConfigurationLocked() { + Configuration config = new Configuration(); + config.fontScale = 0; + if (!computeScreenConfigurationLocked(config)) { + return null; + } + return config; + } + + private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) { + // TODO: Multidisplay: for now only use with default display. + final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation); + if (width < displayInfo.smallestNominalAppWidth) { + displayInfo.smallestNominalAppWidth = width; + } + if (width > displayInfo.largestNominalAppWidth) { + displayInfo.largestNominalAppWidth = width; + } + final int height = mPolicy.getConfigDisplayHeight(dw, dh, rotation); + if (height < displayInfo.smallestNominalAppHeight) { + displayInfo.smallestNominalAppHeight = height; + } + if (height > displayInfo.largestNominalAppHeight) { + displayInfo.largestNominalAppHeight = height; + } + } + + private int reduceConfigLayout(int curLayout, int rotation, float density, + int dw, int dh) { + // TODO: Multidisplay: for now only use with default display. + // Get the app screen size at this rotation. + int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation); + int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation); + + // Compute the screen layout size class for this rotation. + int longSize = w; + int shortSize = h; + if (longSize < shortSize) { + int tmp = longSize; + longSize = shortSize; + shortSize = tmp; + } + longSize = (int)(longSize/density); + shortSize = (int)(shortSize/density); + return Configuration.reduceScreenLayout(curLayout, longSize, shortSize); + } + + private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated, + int dw, int dh, float density, Configuration outConfig) { + // TODO: Multidisplay: for now only use with default display. + + // We need to determine the smallest width that will occur under normal + // operation. To this, start with the base screen size and compute the + // width under the different possible rotations. We need to un-rotate + // the current screen dimensions before doing this. + int unrotDw, unrotDh; + if (rotated) { + unrotDw = dh; + unrotDh = dw; + } else { + unrotDw = dw; + unrotDh = dh; + } + displayInfo.smallestNominalAppWidth = 1<<30; + displayInfo.smallestNominalAppHeight = 1<<30; + displayInfo.largestNominalAppWidth = 0; + displayInfo.largestNominalAppHeight = 0; + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, unrotDw, unrotDh); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, unrotDh, unrotDw); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, unrotDw, unrotDh); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, unrotDh, unrotDw); + int sl = Configuration.resetScreenLayout(outConfig.screenLayout); + sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh); + sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw); + sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh); + sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw); + outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density); + outConfig.screenLayout = sl; + } + + private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm, + int dw, int dh) { + // TODO: Multidisplay: for now only use with default display. + dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation); + dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation); + float scale = CompatibilityInfo.computeCompatibleScaling(dm, null); + int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f); + if (curSize == 0 || size < curSize) { + curSize = size; + } + return curSize; + } + + private int computeCompatSmallestWidth(boolean rotated, DisplayMetrics dm, int dw, int dh) { + // TODO: Multidisplay: for now only use with default display. + mTmpDisplayMetrics.setTo(dm); + final DisplayMetrics tmpDm = mTmpDisplayMetrics; + final int unrotDw, unrotDh; + if (rotated) { + unrotDw = dh; + unrotDh = dw; + } else { + unrotDw = dw; + unrotDh = dh; + } + int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, tmpDm, unrotDw, unrotDh); + sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, tmpDm, unrotDh, unrotDw); + sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, tmpDm, unrotDw, unrotDh); + sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, tmpDm, unrotDh, unrotDw); + return sw; + } + + boolean computeScreenConfigurationLocked(Configuration config) { + if (!mDisplayReady) { + return false; + } + + // TODO(multidisplay): For now, apply Configuration to main screen only. + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + + // Use the effective "visual" dimensions based on current rotation + final boolean rotated = (mRotation == Surface.ROTATION_90 + || mRotation == Surface.ROTATION_270); + final int realdw = rotated ? + displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; + final int realdh = rotated ? + displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; + int dw = realdw; + int dh = realdh; + + if (mAltOrientation) { + if (realdw > realdh) { + // Turn landscape into portrait. + int maxw = (int)(realdh/1.3f); + if (maxw < realdw) { + dw = maxw; + } + } else { + // Turn portrait into landscape. + int maxh = (int)(realdw/1.3f); + if (maxh < realdh) { + dh = maxh; + } + } + } + + if (config != null) { + config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT : + Configuration.ORIENTATION_LANDSCAPE; + } + + // Update application display metrics. + final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); + final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + synchronized(displayContent.mDisplaySizeLock) { + displayInfo.rotation = mRotation; + displayInfo.logicalWidth = dw; + displayInfo.logicalHeight = dh; + displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity; + displayInfo.appWidth = appWidth; + displayInfo.appHeight = appHeight; + displayInfo.getLogicalMetrics(mRealDisplayMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); + displayInfo.getAppMetrics(mDisplayMetrics); + mDisplayManagerService.setDisplayInfoOverrideFromWindowManager( + displayContent.getDisplayId(), displayInfo); + } + if (false) { + Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight); + } + + final DisplayMetrics dm = mDisplayMetrics; + mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm, + mCompatDisplayMetrics); + + if (config != null) { + config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation) + / dm.density); + config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation) + / dm.density); + computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, dm.density, config); + + config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale); + config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale); + config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh); + config.densityDpi = displayContent.mBaseDisplayDensity; + + // Update the configuration based on available input devices, lid switch, + // and platform configuration. + config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; + config.keyboard = Configuration.KEYBOARD_NOKEYS; + config.navigation = Configuration.NAVIGATION_NONAV; + + int keyboardPresence = 0; + int navigationPresence = 0; + final InputDevice[] devices = mInputManager.getInputDevices(); + final int len = devices.length; + for (int i = 0; i < len; i++) { + InputDevice device = devices[i]; + if (!device.isVirtual()) { + final int sources = device.getSources(); + final int presenceFlag = device.isExternal() ? + WindowManagerPolicy.PRESENCE_EXTERNAL : + WindowManagerPolicy.PRESENCE_INTERNAL; + + if (mIsTouchDevice) { + if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == + InputDevice.SOURCE_TOUCHSCREEN) { + config.touchscreen = Configuration.TOUCHSCREEN_FINGER; + } + } else { + config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; + } + + if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) { + config.navigation = Configuration.NAVIGATION_TRACKBALL; + navigationPresence |= presenceFlag; + } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD + && config.navigation == Configuration.NAVIGATION_NONAV) { + config.navigation = Configuration.NAVIGATION_DPAD; + navigationPresence |= presenceFlag; + } + + if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) { + config.keyboard = Configuration.KEYBOARD_QWERTY; + keyboardPresence |= presenceFlag; + } + } + } + + // Determine whether a hard keyboard is available and enabled. + boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS; + if (hardKeyboardAvailable != mHardKeyboardAvailable) { + mHardKeyboardAvailable = hardKeyboardAvailable; + mHardKeyboardEnabled = hardKeyboardAvailable; + mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); + mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); + } + if (!mHardKeyboardEnabled) { + config.keyboard = Configuration.KEYBOARD_NOKEYS; + } + + // Let the policy update hidden states. + config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; + config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; + config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO; + mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence); + } + + return true; + } + + public boolean isHardKeyboardAvailable() { + synchronized (mWindowMap) { + return mHardKeyboardAvailable; + } + } + + public boolean isHardKeyboardEnabled() { + synchronized (mWindowMap) { + return mHardKeyboardEnabled; + } + } + + public void setHardKeyboardEnabled(boolean enabled) { + synchronized (mWindowMap) { + if (mHardKeyboardEnabled != enabled) { + mHardKeyboardEnabled = enabled; + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + } + + public void setOnHardKeyboardStatusChangeListener( + OnHardKeyboardStatusChangeListener listener) { + synchronized (mWindowMap) { + mHardKeyboardStatusChangeListener = listener; + } + } + + void notifyHardKeyboardStatusChange() { + final boolean available, enabled; + final OnHardKeyboardStatusChangeListener listener; + synchronized (mWindowMap) { + listener = mHardKeyboardStatusChangeListener; + available = mHardKeyboardAvailable; + enabled = mHardKeyboardEnabled; + } + if (listener != null) { + listener.onHardKeyboardStatusChange(available, enabled); + } + } + + // ------------------------------------------------------------- + // Drag and drop + // ------------------------------------------------------------- + + IBinder prepareDragSurface(IWindow window, SurfaceSession session, + int flags, int width, int height, Surface outSurface) { + if (DEBUG_DRAG) { + Slog.d(TAG, "prepare drag surface: w=" + width + " h=" + height + + " flags=" + Integer.toHexString(flags) + " win=" + window + + " asbinder=" + window.asBinder()); + } + + final int callerPid = Binder.getCallingPid(); + final long origId = Binder.clearCallingIdentity(); + IBinder token = null; + + try { + synchronized (mWindowMap) { + try { + if (mDragState == null) { + // TODO(multi-display): support other displays + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final Display display = displayContent.getDisplay(); + SurfaceControl surface = new SurfaceControl(session, "drag surface", + width, height, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); + surface.setLayerStack(display.getLayerStack()); + if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG " + + surface + ": CREATE"); + outSurface.copyFrom(surface); + final IBinder winBinder = window.asBinder(); + token = new Binder(); + mDragState = new DragState(this, token, surface, /*flags*/ 0, winBinder); + token = mDragState.mToken = new Binder(); + + // 5 second timeout for this window to actually begin the drag + mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder); + Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder); + mH.sendMessageDelayed(msg, 5000); + } else { + Slog.w(TAG, "Drag already in progress"); + } + } catch (OutOfResourcesException e) { + Slog.e(TAG, "Can't allocate drag surface w=" + width + " h=" + height, e); + if (mDragState != null) { + mDragState.reset(); + mDragState = null; + } + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + return token; + } + + // ------------------------------------------------------------- + // Input Events and Focus Management + // ------------------------------------------------------------- + + final InputMonitor mInputMonitor = new InputMonitor(this); + private boolean mEventDispatchingEnabled; + + @Override + public void pauseKeyDispatching(IBinder _token) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "pauseKeyDispatching()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized (mWindowMap) { + WindowToken token = mTokenMap.get(_token); + if (token != null) { + mInputMonitor.pauseDispatchingLw(token); + } + } + } + + @Override + public void resumeKeyDispatching(IBinder _token) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "resumeKeyDispatching()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized (mWindowMap) { + WindowToken token = mTokenMap.get(_token); + if (token != null) { + mInputMonitor.resumeDispatchingLw(token); + } + } + } + + @Override + public void setEventDispatching(boolean enabled) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "setEventDispatching()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized (mWindowMap) { + mEventDispatchingEnabled = enabled; + if (mDisplayEnabled) { + mInputMonitor.setEventDispatchingLw(enabled); + } + sendScreenStatusToClientsLocked(); + } + } + + @Override + public IBinder getFocusedWindowToken() { + if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, + "getFocusedWindowToken()")) { + throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); + } + synchronized (mWindowMap) { + WindowState windowState = getFocusedWindowLocked(); + if (windowState != null) { + return windowState.mClient.asBinder(); + } + return null; + } + } + + private WindowState getFocusedWindow() { + synchronized (mWindowMap) { + return getFocusedWindowLocked(); + } + } + + private WindowState getFocusedWindowLocked() { + return mCurrentFocus; + } + + public boolean detectSafeMode() { + if (!mInputMonitor.waitForInputDevicesReady( + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) { + Slog.w(TAG, "Devices still not ready after waiting " + + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS + + " milliseconds before attempting to detect safe mode."); + } + + int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, + KeyEvent.KEYCODE_MENU); + int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S); + int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, + KeyEvent.KEYCODE_DPAD_CENTER); + int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, + InputManagerService.BTN_MOUSE); + int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, + KeyEvent.KEYCODE_VOLUME_DOWN); + mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0 + || volumeDownState > 0; + try { + if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) { + mSafeMode = true; + SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, ""); + } + } catch (IllegalArgumentException e) { + } + if (mSafeMode) { + Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState + + " dpad=" + dpadState + " trackball=" + trackballState + ")"); + } else { + Log.i(TAG, "SAFE MODE not enabled"); + } + mPolicy.setSafeMode(mSafeMode); + return mSafeMode; + } + + public void displayReady() { + displayReady(Display.DEFAULT_DISPLAY); + + synchronized(mWindowMap) { + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + readForcedDisplaySizeAndDensityLocked(displayContent); + mDisplayReady = true; + } + + try { + mActivityManager.updateConfiguration(null); + } catch (RemoteException e) { + } + + synchronized(mWindowMap) { + mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TOUCHSCREEN); + configureDisplayPolicyLocked(getDefaultDisplayContentLocked()); + } + + try { + mActivityManager.updateConfiguration(null); + } catch (RemoteException e) { + } + } + + private void displayReady(int displayId) { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + mAnimator.addDisplayLocked(displayId); + synchronized(displayContent.mDisplaySizeLock) { + // Bootstrap the default logical display from the display manager. + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId); + if (newDisplayInfo != null) { + displayInfo.copyFrom(newDisplayInfo); + } + displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; + displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; + displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; + displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth; + displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight; + displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity; + displayContent.mBaseDisplayRect.set(0, 0, + displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight); + } + } + } + } + + public void systemReady() { + mPolicy.systemReady(); + } + + // TODO(multidisplay): Call isScreenOn for each display. + private void sendScreenStatusToClientsLocked() { + final boolean on = mPowerManager.isScreenOn(); + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + try { + windows.get(winNdx).mClient.dispatchScreenState(on); + } catch (RemoteException e) { + // Ignored + } + } + } + } + + // ------------------------------------------------------------- + // Async Handler + // ------------------------------------------------------------- + + final class H extends Handler { + public static final int REPORT_FOCUS_CHANGE = 2; + public static final int REPORT_LOSING_FOCUS = 3; + public static final int DO_TRAVERSAL = 4; + public static final int ADD_STARTING = 5; + public static final int REMOVE_STARTING = 6; + public static final int FINISHED_STARTING = 7; + public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8; + public static final int REPORT_APPLICATION_TOKEN_DRAWN = 9; + public static final int WINDOW_FREEZE_TIMEOUT = 11; + + public static final int APP_TRANSITION_TIMEOUT = 13; + public static final int PERSIST_ANIMATION_SCALE = 14; + public static final int FORCE_GC = 15; + public static final int ENABLE_SCREEN = 16; + public static final int APP_FREEZE_TIMEOUT = 17; + public static final int SEND_NEW_CONFIGURATION = 18; + public static final int REPORT_WINDOWS_CHANGE = 19; + public static final int DRAG_START_TIMEOUT = 20; + public static final int DRAG_END_TIMEOUT = 21; + public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22; + public static final int BOOT_TIMEOUT = 23; + public static final int WAITING_FOR_DRAWN_TIMEOUT = 24; + public static final int SHOW_STRICT_MODE_VIOLATION = 25; + public static final int DO_ANIMATION_CALLBACK = 26; + + public static final int DO_DISPLAY_ADDED = 27; + public static final int DO_DISPLAY_REMOVED = 28; + public static final int DO_DISPLAY_CHANGED = 29; + + public static final int CLIENT_FREEZE_TIMEOUT = 30; + public static final int TAP_OUTSIDE_STACK = 31; + public static final int NOTIFY_ACTIVITY_DRAWN = 32; + + public static final int REMOVE_STARTING_TIMEOUT = 33; + + @Override + public void handleMessage(Message msg) { + if (DEBUG_WINDOW_TRACE) { + Slog.v(TAG, "handleMessage: entry what=" + msg.what); + } + switch (msg.what) { + case REPORT_FOCUS_CHANGE: { + WindowState lastFocus; + WindowState newFocus; + + synchronized(mWindowMap) { + lastFocus = mLastFocus; + newFocus = mCurrentFocus; + if (lastFocus == newFocus) { + // Focus is not changing, so nothing to do. + return; + } + mLastFocus = newFocus; + if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Focus moving from " + lastFocus + + " to " + newFocus); + if (newFocus != null && lastFocus != null + && !newFocus.isDisplayedLw()) { + //Slog.i(TAG, "Delaying loss of focus..."); + mLosingFocus.add(lastFocus); + lastFocus = null; + } + } + + //System.out.println("Changing focus from " + lastFocus + // + " to " + newFocus); + if (newFocus != null) { + if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Gaining focus: " + newFocus); + newFocus.reportFocusChangedSerialized(true, mInTouchMode); + notifyFocusChanged(); + } + + if (lastFocus != null) { + if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Losing focus: " + lastFocus); + lastFocus.reportFocusChangedSerialized(false, mInTouchMode); + } + } break; + + case REPORT_LOSING_FOCUS: { + ArrayList<WindowState> losers; + + synchronized(mWindowMap) { + losers = mLosingFocus; + mLosingFocus = new ArrayList<WindowState>(); + } + + final int N = losers.size(); + for (int i=0; i<N; i++) { + if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Losing delayed focus: " + + losers.get(i)); + losers.get(i).reportFocusChangedSerialized(false, mInTouchMode); + } + } break; + + case DO_TRAVERSAL: { + synchronized(mWindowMap) { + mTraversalScheduled = false; + performLayoutAndPlaceSurfacesLocked(); + } + } break; + + case ADD_STARTING: { + final AppWindowToken wtoken = (AppWindowToken)msg.obj; + final StartingData sd = wtoken.startingData; + + if (sd == null) { + // Animation has been canceled... do nothing. + return; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Add starting " + + wtoken + ": pkg=" + sd.pkg); + + View view = null; + try { + view = mPolicy.addStartingWindow( + wtoken.token, sd.pkg, sd.theme, sd.compatInfo, + sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo, sd.windowFlags); + } catch (Exception e) { + Slog.w(TAG, "Exception when adding starting window", e); + } + + if (view != null) { + boolean abort = false; + + synchronized(mWindowMap) { + if (wtoken.removed || wtoken.startingData == null) { + // If the window was successfully added, then + // we need to remove it. + if (wtoken.startingWindow != null) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, + "Aborted starting " + wtoken + + ": removed=" + wtoken.removed + + " startingData=" + wtoken.startingData); + removeStartingWindowTimeout(wtoken); + wtoken.startingWindow = null; + wtoken.startingData = null; + abort = true; + } + } else { + wtoken.startingView = view; + } + if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG, + "Added starting " + wtoken + + ": startingWindow=" + + wtoken.startingWindow + " startingView=" + + wtoken.startingView); + } + + if (abort) { + try { + mPolicy.removeStartingWindow(wtoken.token, view); + } catch (Exception e) { + Slog.w(TAG, "Exception when removing starting window", e); + } + } + } + } break; + + case REMOVE_STARTING_TIMEOUT: { + final AppWindowToken wtoken = (AppWindowToken)msg.obj; + Slog.e(TAG, "Starting window " + wtoken + " timed out"); + // Fall through. + } + case REMOVE_STARTING: { + final AppWindowToken wtoken = (AppWindowToken)msg.obj; + IBinder token = null; + View view = null; + synchronized (mWindowMap) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Remove starting " + + wtoken + ": startingWindow=" + + wtoken.startingWindow + " startingView=" + + wtoken.startingView); + if (wtoken.startingWindow != null) { + view = wtoken.startingView; + token = wtoken.token; + wtoken.startingData = null; + wtoken.startingView = null; + wtoken.startingWindow = null; + wtoken.startingDisplayed = false; + } + } + if (view != null) { + try { + mPolicy.removeStartingWindow(token, view); + } catch (Exception e) { + Slog.w(TAG, "Exception when removing starting window", e); + } + } + } break; + + case FINISHED_STARTING: { + IBinder token = null; + View view = null; + while (true) { + synchronized (mWindowMap) { + final int N = mFinishedStarting.size(); + if (N <= 0) { + break; + } + AppWindowToken wtoken = mFinishedStarting.remove(N-1); + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, + "Finished starting " + wtoken + + ": startingWindow=" + wtoken.startingWindow + + " startingView=" + wtoken.startingView); + + if (wtoken.startingWindow == null) { + continue; + } + + view = wtoken.startingView; + token = wtoken.token; + wtoken.startingData = null; + wtoken.startingView = null; + wtoken.startingWindow = null; + wtoken.startingDisplayed = false; + } + + try { + mPolicy.removeStartingWindow(token, view); + } catch (Exception e) { + Slog.w(TAG, "Exception when removing starting window", e); + } + } + } break; + + case REPORT_APPLICATION_TOKEN_DRAWN: { + final AppWindowToken wtoken = (AppWindowToken)msg.obj; + + try { + if (DEBUG_VISIBILITY) Slog.v( + TAG, "Reporting drawn in " + wtoken); + wtoken.appToken.windowsDrawn(); + } catch (RemoteException ex) { + } + } break; + + case REPORT_APPLICATION_TOKEN_WINDOWS: { + final AppWindowToken wtoken = (AppWindowToken)msg.obj; + + boolean nowVisible = msg.arg1 != 0; + boolean nowGone = msg.arg2 != 0; + + try { + if (DEBUG_VISIBILITY) Slog.v( + TAG, "Reporting visible in " + wtoken + + " visible=" + nowVisible + + " gone=" + nowGone); + if (nowVisible) { + wtoken.appToken.windowsVisible(); + } else { + wtoken.appToken.windowsGone(); + } + } catch (RemoteException ex) { + } + } break; + + case WINDOW_FREEZE_TIMEOUT: { + // TODO(multidisplay): Can non-default displays rotate? + synchronized (mWindowMap) { + Slog.w(TAG, "Window freeze timeout expired."); + final WindowList windows = getDefaultWindowListLocked(); + int i = windows.size(); + while (i > 0) { + i--; + WindowState w = windows.get(i); + if (w.mOrientationChanging) { + w.mOrientationChanging = false; + w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); + Slog.w(TAG, "Force clearing orientation change: " + w); + } + } + performLayoutAndPlaceSurfacesLocked(); + } + break; + } + + case APP_TRANSITION_TIMEOUT: { + synchronized (mWindowMap) { + if (mAppTransition.isTransitionSet()) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** APP TRANSITION TIMEOUT"); + mAppTransition.setTimeout(); + performLayoutAndPlaceSurfacesLocked(); + } + } + break; + } + + case PERSIST_ANIMATION_SCALE: { + Settings.Global.putFloat(mContext.getContentResolver(), + Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); + Settings.Global.putFloat(mContext.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); + Settings.Global.putFloat(mContext.getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale); + break; + } + + case FORCE_GC: { + synchronized (mWindowMap) { + // Since we're holding both mWindowMap and mAnimator we don't need to + // hold mAnimator.mLayoutToAnim. + if (mAnimator.mAnimating || mAnimationScheduled) { + // If we are animating, don't do the gc now but + // delay a bit so we don't interrupt the animation. + sendEmptyMessageDelayed(H.FORCE_GC, 2000); + return; + } + // If we are currently rotating the display, it will + // schedule a new message when done. + if (mDisplayFrozen) { + return; + } + } + Runtime.getRuntime().gc(); + break; + } + + case ENABLE_SCREEN: { + performEnableScreen(); + break; + } + + case APP_FREEZE_TIMEOUT: { + synchronized (mWindowMap) { + Slog.w(TAG, "App freeze timeout expired."); + DisplayContent displayContent = getDefaultDisplayContentLocked(); + final ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + AppWindowToken tok = tokens.get(tokenNdx); + if (tok.mAppAnimator.freezingScreen) { + Slog.w(TAG, "Force clearing freeze: " + tok); + unsetAppFreezingScreenLocked(tok, true, true); + } + } + } + } + break; + } + + case CLIENT_FREEZE_TIMEOUT: { + synchronized (mWindowMap) { + if (mClientFreezingScreen) { + mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client-timeout"; + stopFreezingDisplayLocked(); + } + } + break; + } + + case SEND_NEW_CONFIGURATION: { + removeMessages(SEND_NEW_CONFIGURATION); + sendNewConfiguration(); + break; + } + + case REPORT_WINDOWS_CHANGE: { + if (mWindowsChanged) { + synchronized (mWindowMap) { + mWindowsChanged = false; + } + notifyWindowsChanged(); + } + break; + } + + case DRAG_START_TIMEOUT: { + IBinder win = (IBinder)msg.obj; + if (DEBUG_DRAG) { + Slog.w(TAG, "Timeout starting drag by win " + win); + } + synchronized (mWindowMap) { + // !!! TODO: ANR the app that has failed to start the drag in time + if (mDragState != null) { + mDragState.unregister(); + mInputMonitor.updateInputWindowsLw(true /*force*/); + mDragState.reset(); + mDragState = null; + } + } + break; + } + + case DRAG_END_TIMEOUT: { + IBinder win = (IBinder)msg.obj; + if (DEBUG_DRAG) { + Slog.w(TAG, "Timeout ending drag to win " + win); + } + synchronized (mWindowMap) { + // !!! TODO: ANR the drag-receiving app + if (mDragState != null) { + mDragState.mDragResult = false; + mDragState.endDragLw(); + } + } + break; + } + + case REPORT_HARD_KEYBOARD_STATUS_CHANGE: { + notifyHardKeyboardStatusChange(); + break; + } + + case BOOT_TIMEOUT: { + performBootTimeout(); + break; + } + + case WAITING_FOR_DRAWN_TIMEOUT: { + Pair<WindowState, IRemoteCallback> pair; + synchronized (mWindowMap) { + pair = (Pair<WindowState, IRemoteCallback>)msg.obj; + Slog.w(TAG, "Timeout waiting for drawn: " + pair.first); + if (!mWaitingForDrawn.remove(pair)) { + return; + } + } + try { + pair.second.sendResult(null); + } catch (RemoteException e) { + } + break; + } + + case SHOW_STRICT_MODE_VIOLATION: { + showStrictModeViolation(msg.arg1, msg.arg2); + break; + } + + case DO_ANIMATION_CALLBACK: { + try { + ((IRemoteCallback)msg.obj).sendResult(null); + } catch (RemoteException e) { + } + break; + } + + case DO_DISPLAY_ADDED: + synchronized (mWindowMap) { + handleDisplayAddedLocked(msg.arg1); + } + break; + + case DO_DISPLAY_REMOVED: + synchronized (mWindowMap) { + handleDisplayRemovedLocked(msg.arg1); + } + break; + + case DO_DISPLAY_CHANGED: + synchronized (mWindowMap) { + handleDisplayChangedLocked(msg.arg1); + } + break; + + case TAP_OUTSIDE_STACK: { + int stackId; + synchronized (mWindowMap) { + stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2); + } + if (stackId >= 0) { + try { + mActivityManager.setFocusedStack(stackId); + } catch (RemoteException e) { + } + } + } + break; + case NOTIFY_ACTIVITY_DRAWN: + try { + mActivityManager.notifyActivityDrawn((IBinder) msg.obj); + } catch (RemoteException e) { + } + break; + } + if (DEBUG_WINDOW_TRACE) { + Slog.v(TAG, "handleMessage: exit"); + } + } + } + + // ------------------------------------------------------------- + // IWindowManager API + // ------------------------------------------------------------- + + @Override + public IWindowSession openSession(IInputMethodClient client, + IInputContext inputContext) { + if (client == null) throw new IllegalArgumentException("null client"); + if (inputContext == null) throw new IllegalArgumentException("null inputContext"); + Session session = new Session(this, client, inputContext); + return session; + } + + @Override + public boolean inputMethodClientHasFocus(IInputMethodClient client) { + synchronized (mWindowMap) { + // The focus for the client is the window immediately below + // where we would place the input method window. + int idx = findDesiredInputMethodWindowIndexLocked(false); + if (idx > 0) { + // TODO(multidisplay): IMEs are only supported on the default display. + WindowState imFocus = getDefaultWindowListLocked().get(idx-1); + if (DEBUG_INPUT_METHOD) { + Slog.i(TAG, "Desired input method target: " + imFocus); + Slog.i(TAG, "Current focus: " + mCurrentFocus); + Slog.i(TAG, "Last focus: " + mLastFocus); + } + if (imFocus != null) { + // This may be a starting window, in which case we still want + // to count it as okay. + if (imFocus.mAttrs.type == LayoutParams.TYPE_APPLICATION_STARTING + && imFocus.mAppToken != null) { + // The client has definitely started, so it really should + // have a window in this app token. Let's look for it. + for (int i=0; i<imFocus.mAppToken.windows.size(); i++) { + WindowState w = imFocus.mAppToken.windows.get(i); + if (w != imFocus) { + Log.i(TAG, "Switching to real app window: " + w); + imFocus = w; + break; + } + } + } + if (DEBUG_INPUT_METHOD) { + Slog.i(TAG, "IM target client: " + imFocus.mSession.mClient); + if (imFocus.mSession.mClient != null) { + Slog.i(TAG, "IM target client binder: " + + imFocus.mSession.mClient.asBinder()); + Slog.i(TAG, "Requesting client binder: " + client.asBinder()); + } + } + if (imFocus.mSession.mClient != null && + imFocus.mSession.mClient.asBinder() == client.asBinder()) { + return true; + } + } + } + + // Okay, how about this... what is the current focus? + // It seems in some cases we may not have moved the IM + // target window, such as when it was in a pop-up window, + // so let's also look at the current focus. (An example: + // go to Gmail, start searching so the keyboard goes up, + // press home. Sometimes the IME won't go down.) + // Would be nice to fix this more correctly, but it's + // way at the end of a release, and this should be good enough. + if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null + && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) { + return true; + } + } + return false; + } + + @Override + public void getInitialDisplaySize(int displayId, Point size) { + synchronized (mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + synchronized(displayContent.mDisplaySizeLock) { + size.x = displayContent.mInitialDisplayWidth; + size.y = displayContent.mInitialDisplayHeight; + } + } + } + } + + @Override + public void getBaseDisplaySize(int displayId, Point size) { + synchronized (mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + synchronized(displayContent.mDisplaySizeLock) { + size.x = displayContent.mBaseDisplayWidth; + size.y = displayContent.mBaseDisplayHeight; + } + } + } + } + + @Override + public void setForcedDisplaySize(int displayId, int width, int height) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + + android.Manifest.permission.WRITE_SECURE_SETTINGS); + } + if (displayId != Display.DEFAULT_DISPLAY) { + throw new IllegalArgumentException("Can only set the default display"); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + // Set some sort of reasonable bounds on the size of the display that we + // will try to emulate. + final int MIN_WIDTH = 200; + final int MIN_HEIGHT = 200; + final int MAX_SCALE = 2; + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + width = Math.min(Math.max(width, MIN_WIDTH), + displayContent.mInitialDisplayWidth * MAX_SCALE); + height = Math.min(Math.max(height, MIN_HEIGHT), + displayContent.mInitialDisplayHeight * MAX_SCALE); + setForcedDisplaySizeLocked(displayContent, width, height); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) { + String sizeStr = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.DISPLAY_SIZE_FORCED); + if (sizeStr == null || sizeStr.length() == 0) { + sizeStr = SystemProperties.get(SIZE_OVERRIDE, null); + } + if (sizeStr != null && sizeStr.length() > 0) { + final int pos = sizeStr.indexOf(','); + if (pos > 0 && sizeStr.lastIndexOf(',') == pos) { + int width, height; + try { + width = Integer.parseInt(sizeStr.substring(0, pos)); + height = Integer.parseInt(sizeStr.substring(pos+1)); + synchronized(displayContent.mDisplaySizeLock) { + if (displayContent.mBaseDisplayWidth != width + || displayContent.mBaseDisplayHeight != height) { + Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height); + displayContent.mBaseDisplayWidth = width; + displayContent.mBaseDisplayHeight = height; + } + } + } catch (NumberFormatException ex) { + } + } + } + String densityStr = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.DISPLAY_DENSITY_FORCED); + if (densityStr == null || densityStr.length() == 0) { + densityStr = SystemProperties.get(DENSITY_OVERRIDE, null); + } + if (densityStr != null && densityStr.length() > 0) { + int density; + try { + density = Integer.parseInt(densityStr); + synchronized(displayContent.mDisplaySizeLock) { + if (displayContent.mBaseDisplayDensity != density) { + Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density); + displayContent.mBaseDisplayDensity = density; + } + } + } catch (NumberFormatException ex) { + } + } + } + + // displayContent must not be null + private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) { + Slog.i(TAG, "Using new display size: " + width + "x" + height); + + synchronized(displayContent.mDisplaySizeLock) { + displayContent.mBaseDisplayWidth = width; + displayContent.mBaseDisplayHeight = height; + } + reconfigureDisplayLocked(displayContent); + } + + @Override + public void clearForcedDisplaySize(int displayId) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + + android.Manifest.permission.WRITE_SECURE_SETTINGS); + } + if (displayId != Display.DEFAULT_DISPLAY) { + throw new IllegalArgumentException("Can only set the default display"); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth, + displayContent.mInitialDisplayHeight); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_SIZE_FORCED, ""); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public int getInitialDisplayDensity(int displayId) { + synchronized (mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + synchronized(displayContent.mDisplaySizeLock) { + return displayContent.mInitialDisplayDensity; + } + } + } + return -1; + } + + @Override + public int getBaseDisplayDensity(int displayId) { + synchronized (mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + synchronized(displayContent.mDisplaySizeLock) { + return displayContent.mBaseDisplayDensity; + } + } + } + return -1; + } + + @Override + public void setForcedDisplayDensity(int displayId, int density) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + + android.Manifest.permission.WRITE_SECURE_SETTINGS); + } + if (displayId != Display.DEFAULT_DISPLAY) { + throw new IllegalArgumentException("Can only set the default display"); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + setForcedDisplayDensityLocked(displayContent, density); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density)); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // displayContent must not be null + private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) { + Slog.i(TAG, "Using new display density: " + density); + + synchronized(displayContent.mDisplaySizeLock) { + displayContent.mBaseDisplayDensity = density; + } + reconfigureDisplayLocked(displayContent); + } + + @Override + public void clearForcedDisplayDensity(int displayId) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + + android.Manifest.permission.WRITE_SECURE_SETTINGS); + } + if (displayId != Display.DEFAULT_DISPLAY) { + throw new IllegalArgumentException("Can only set the default display"); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + setForcedDisplayDensityLocked(displayContent, + displayContent.mInitialDisplayDensity); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_DENSITY_FORCED, ""); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // displayContent must not be null + private void reconfigureDisplayLocked(DisplayContent displayContent) { + // TODO: Multidisplay: for now only use with default display. + configureDisplayPolicyLocked(displayContent); + displayContent.layoutNeeded = true; + + boolean configChanged = updateOrientationFromAppTokensLocked(false); + mTempConfiguration.setToDefaults(); + mTempConfiguration.fontScale = mCurConfiguration.fontScale; + if (computeScreenConfigurationLocked(mTempConfiguration)) { + if (mCurConfiguration.diff(mTempConfiguration) != 0) { + configChanged = true; + } + } + + if (configChanged) { + mWaitingForConfig = true; + startFreezingDisplayLocked(false, 0, 0); + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + + performLayoutAndPlaceSurfacesLocked(); + } + + private void configureDisplayPolicyLocked(DisplayContent displayContent) { + mPolicy.setInitialDisplaySize(displayContent.getDisplay(), + displayContent.mBaseDisplayWidth, + displayContent.mBaseDisplayHeight, + displayContent.mBaseDisplayDensity); + + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + mPolicy.setDisplayOverscan(displayContent.getDisplay(), + displayInfo.overscanLeft, displayInfo.overscanTop, + displayInfo.overscanRight, displayInfo.overscanBottom); + } + + @Override + public void setOverscan(int displayId, int left, int top, int right, int bottom) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + + android.Manifest.permission.WRITE_SECURE_SETTINGS); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + setOverscanLocked(displayContent, left, top, right, bottom); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setOverscanLocked(DisplayContent displayContent, + int left, int top, int right, int bottom) { + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + synchronized (displayContent.mDisplaySizeLock) { + displayInfo.overscanLeft = left; + displayInfo.overscanTop = top; + displayInfo.overscanRight = right; + displayInfo.overscanBottom = bottom; + } + + mDisplaySettings.setOverscanLocked(displayInfo.name, left, top, right, bottom); + mDisplaySettings.writeSettingsLocked(); + + reconfigureDisplayLocked(displayContent); + } + + // ------------------------------------------------------------- + // Internals + // ------------------------------------------------------------- + + final WindowState windowForClientLocked(Session session, IWindow client, + boolean throwOnError) { + return windowForClientLocked(session, client.asBinder(), throwOnError); + } + + final WindowState windowForClientLocked(Session session, IBinder client, + boolean throwOnError) { + WindowState win = mWindowMap.get(client); + if (localLOGV) Slog.v( + TAG, "Looking up client " + client + ": " + win); + if (win == null) { + RuntimeException ex = new IllegalArgumentException( + "Requested window " + client + " does not exist"); + if (throwOnError) { + throw ex; + } + Slog.w(TAG, "Failed looking up window", ex); + return null; + } + if (session != null && win.mSession != session) { + RuntimeException ex = new IllegalArgumentException( + "Requested window " + client + " is in session " + + win.mSession + ", not " + session); + if (throwOnError) { + throw ex; + } + Slog.w(TAG, "Failed looking up window", ex); + return null; + } + + return win; + } + + final void rebuildAppWindowListLocked() { + // TODO: Multidisplay, when ActivityStacks and tasks exist on more than one display. + rebuildAppWindowListLocked(getDefaultDisplayContentLocked()); + } + + private void rebuildAppWindowListLocked(final DisplayContent displayContent) { + final WindowList windows = displayContent.getWindowList(); + int NW = windows.size(); + int i; + int lastBelow = -1; + int numRemoved = 0; + + if (mRebuildTmp.length < NW) { + mRebuildTmp = new WindowState[NW+10]; + } + + // First remove all existing app windows. + i=0; + while (i < NW) { + WindowState w = windows.get(i); + if (w.mAppToken != null) { + WindowState win = windows.remove(i); + win.mRebuilding = true; + mRebuildTmp[numRemoved] = win; + mWindowsChanged = true; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Rebuild removing window: " + win); + NW--; + numRemoved++; + continue; + } else if (lastBelow == i-1) { + if (w.mAttrs.type == TYPE_WALLPAPER || w.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) { + lastBelow = i; + } + } + i++; + } + + // Keep whatever windows were below the app windows still below, + // by skipping them. + lastBelow++; + i = lastBelow; + + // First add all of the exiting app tokens... these are no longer + // in the main app list, but still have windows shown. We put them + // in the back because now that the animation is over we no longer + // will care about them. + AppTokenList exitingAppTokens = displayContent.mExitingAppTokens; + int NT = exitingAppTokens.size(); + for (int j=0; j<NT; j++) { + i = reAddAppWindowsLocked(displayContent, i, exitingAppTokens.get(j)); + } + + // And add in the still active app tokens in Z order. + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + i = reAddAppWindowsLocked(displayContent, i, wtoken); + } + } + + i -= lastBelow; + if (i != numRemoved) { + Slog.w(TAG, "Rebuild removed " + numRemoved + " windows but added " + i, + new RuntimeException("here").fillInStackTrace()); + for (i=0; i<numRemoved; i++) { + WindowState ws = mRebuildTmp[i]; + if (ws.mRebuilding) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 1024); + ws.dump(pw, "", true); + pw.flush(); + Slog.w(TAG, "This window was lost: " + ws); + Slog.w(TAG, sw.toString()); + ws.mWinAnimator.destroySurfaceLocked(); + } + } + Slog.w(TAG, "Current app token list:"); + dumpAppTokensLocked(); + Slog.w(TAG, "Final window list:"); + dumpWindowsLocked(); + } + } + + private final void assignLayersLocked(WindowList windows) { + int N = windows.size(); + int curBaseLayer = 0; + int curLayer = 0; + int i; + + if (DEBUG_LAYERS) Slog.v(TAG, "Assigning layers based on windows=" + windows, + new RuntimeException("here").fillInStackTrace()); + + boolean anyLayerChanged = false; + + for (i=0; i<N; i++) { + final WindowState w = windows.get(i); + final WindowStateAnimator winAnimator = w.mWinAnimator; + boolean layerChanged = false; + int oldLayer = w.mLayer; + if (w.mBaseLayer == curBaseLayer || w.mIsImWindow + || (i > 0 && w.mIsWallpaper)) { + curLayer += WINDOW_LAYER_MULTIPLIER; + w.mLayer = curLayer; + } else { + curBaseLayer = curLayer = w.mBaseLayer; + w.mLayer = curLayer; + } + if (w.mLayer != oldLayer) { + layerChanged = true; + anyLayerChanged = true; + } + final AppWindowToken wtoken = w.mAppToken; + oldLayer = winAnimator.mAnimLayer; + if (w.mTargetAppToken != null) { + winAnimator.mAnimLayer = + w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment; + } else if (wtoken != null) { + winAnimator.mAnimLayer = + w.mLayer + wtoken.mAppAnimator.animLayerAdjustment; + } else { + winAnimator.mAnimLayer = w.mLayer; + } + if (w.mIsImWindow) { + winAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment; + } else if (w.mIsWallpaper) { + winAnimator.mAnimLayer += mWallpaperAnimLayerAdjustment; + } + if (winAnimator.mAnimLayer != oldLayer) { + layerChanged = true; + anyLayerChanged = true; + } + if (layerChanged && w.getStack().isDimming(winAnimator)) { + // Force an animation pass just to update the mDimLayer layer. + scheduleAnimationLocked(); + } + if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": " + + "mBase=" + w.mBaseLayer + + " mLayer=" + w.mLayer + + (wtoken == null ? + "" : " mAppLayer=" + wtoken.mAppAnimator.animLayerAdjustment) + + " =mAnimLayer=" + winAnimator.mAnimLayer); + //System.out.println( + // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); + } + + //TODO (multidisplay): Magnification is supported only for the default display. + if (mDisplayMagnifier != null && anyLayerChanged + && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowLayersChangedLocked(); + } + } + + private final void performLayoutAndPlaceSurfacesLocked() { + int loopCount = 6; + do { + mTraversalScheduled = false; + performLayoutAndPlaceSurfacesLockedLoop(); + mH.removeMessages(H.DO_TRAVERSAL); + loopCount--; + } while (mTraversalScheduled && loopCount > 0); + mInnerFields.mWallpaperActionPending = false; + } + + private boolean mInLayout = false; + private final void performLayoutAndPlaceSurfacesLockedLoop() { + if (mInLayout) { + if (DEBUG) { + throw new RuntimeException("Recursive call!"); + } + Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers=" + + Debug.getCallers(3)); + return; + } + + if (mWaitingForConfig) { + // Our configuration has changed (most likely rotation), but we + // don't yet have the complete configuration to report to + // applications. Don't do any window layout until we have it. + return; + } + + if (!mDisplayReady) { + // Not yet initialized, nothing to do. + return; + } + + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout"); + mInLayout = true; + boolean recoveringMemory = false; + + try { + if (mForceRemoves != null) { + recoveringMemory = true; + // Wait a little bit for things to settle down, and off we go. + for (int i=0; i<mForceRemoves.size(); i++) { + WindowState ws = mForceRemoves.get(i); + Slog.i(TAG, "Force removing: " + ws); + removeWindowInnerLocked(ws.mSession, ws); + } + mForceRemoves = null; + Slog.w(TAG, "Due to memory failure, waiting a bit for next layout"); + Object tmp = new Object(); + synchronized (tmp) { + try { + tmp.wait(250); + } catch (InterruptedException e) { + } + } + } + } catch (RuntimeException e) { + Log.wtf(TAG, "Unhandled exception while force removing for memory", e); + } + + try { + performLayoutAndPlaceSurfacesLockedInner(recoveringMemory); + + mInLayout = false; + + if (needsLayout()) { + if (++mLayoutRepeatCount < 6) { + requestTraversalLocked(); + } else { + Slog.e(TAG, "Performed 6 layouts in a row. Skipping"); + mLayoutRepeatCount = 0; + } + } else { + mLayoutRepeatCount = 0; + } + + if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) { + mH.removeMessages(H.REPORT_WINDOWS_CHANGE); + mH.sendEmptyMessage(H.REPORT_WINDOWS_CHANGE); + } + } catch (RuntimeException e) { + mInLayout = false; + Log.wtf(TAG, "Unhandled exception while laying out windows", e); + } + + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + } + + private final void performLayoutLockedInner(final DisplayContent displayContent, + boolean initial, boolean updateInputWindows) { + if (!displayContent.layoutNeeded) { + return; + } + displayContent.layoutNeeded = false; + WindowList windows = displayContent.getWindowList(); + boolean isDefaultDisplay = displayContent.isDefaultDisplay; + + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; + + final int NFW = mFakeWindows.size(); + for (int i=0; i<NFW; i++) { + mFakeWindows.get(i).layout(dw, dh); + } + + final int N = windows.size(); + int i; + + if (DEBUG_LAYOUT) { + Slog.v(TAG, "-------------------------------------"); + Slog.v(TAG, "performLayout: needed=" + + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh); + } + + WindowStateAnimator universeBackground = null; + + mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation); + if (isDefaultDisplay) { + // Not needed on non-default displays. + mSystemDecorLayer = mPolicy.getSystemDecorLayerLw(); + mScreenRect.set(0, 0, dw, dh); + } + + mPolicy.getContentRectLw(mTmpContentRect); + displayContent.resize(mTmpContentRect); + + int seq = mLayoutSeq+1; + if (seq < 0) seq = 0; + mLayoutSeq = seq; + + boolean behindDream = false; + + // First perform layout of any root windows (not attached + // to another window). + int topAttached = -1; + for (i = N-1; i >= 0; i--) { + final WindowState win = windows.get(i); + + // Don't do layout of a window if it is not visible, or + // soon won't be visible, to avoid wasting time and funky + // changes while a window is animating away. + final boolean gone = (behindDream && mPolicy.canBeForceHidden(win, win.mAttrs)) + || win.isGoneForLayoutLw(); + + if (DEBUG_LAYOUT && !win.mLayoutAttached) { + Slog.v(TAG, "1ST PASS " + win + + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame + + " mLayoutAttached=" + win.mLayoutAttached + + " screen changed=" + win.isConfigChanged()); + final AppWindowToken atoken = win.mAppToken; + if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + + win.mViewVisibility + " mRelayoutCalled=" + + win.mRelayoutCalled + " hidden=" + + win.mRootToken.hidden + " hiddenRequested=" + + (atoken != null && atoken.hiddenRequested) + + " mAttachedHidden=" + win.mAttachedHidden); + else Slog.v(TAG, " VIS: mViewVisibility=" + + win.mViewVisibility + " mRelayoutCalled=" + + win.mRelayoutCalled + " hidden=" + + win.mRootToken.hidden + " hiddenRequested=" + + (atoken != null && atoken.hiddenRequested) + + " mAttachedHidden=" + win.mAttachedHidden); + } + + // If this view is GONE, then skip it -- keep the current + // frame, and let the caller know so they can ignore it + // if they want. (We do the normal layout for INVISIBLE + // windows, since that means "perform layout as normal, + // just don't display"). + if (!gone || !win.mHaveFrame || win.mLayoutNeeded + || ((win.isConfigChanged() || win.setInsetsChanged()) && + (win.mAttrs.type == TYPE_KEYGUARD || + win.mAppToken != null && win.mAppToken.layoutConfigChanges)) + || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) { + if (!win.mLayoutAttached) { + if (initial) { + //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); + win.mContentChanged = false; + } + if (win.mAttrs.type == TYPE_DREAM) { + // Don't layout windows behind a dream, so that if it + // does stuff like hide the status bar we won't get a + // bad transition when it goes away. + behindDream = true; + } + win.mLayoutNeeded = false; + win.prelayout(); + mPolicy.layoutWindowLw(win, win.mAttrs, null); + win.mLayoutSeq = seq; + if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + + win.mFrame + " mContainingFrame=" + + win.mContainingFrame + " mDisplayFrame=" + + win.mDisplayFrame); + } else { + if (topAttached < 0) topAttached = i; + } + } + if (win.mViewVisibility == View.VISIBLE + && win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND + && universeBackground == null) { + universeBackground = win.mWinAnimator; + } + } + + if (mAnimator.mUniverseBackground != universeBackground) { + mFocusMayChange = true; + mAnimator.mUniverseBackground = universeBackground; + } + + boolean attachedBehindDream = false; + + // Now perform layout of attached windows, which usually + // depend on the position of the window they are attached to. + // XXX does not deal with windows that are attached to windows + // that are themselves attached. + for (i = topAttached; i >= 0; i--) { + final WindowState win = windows.get(i); + + if (win.mLayoutAttached) { + if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win + + " mHaveFrame=" + win.mHaveFrame + + " mViewVisibility=" + win.mViewVisibility + + " mRelayoutCalled=" + win.mRelayoutCalled); + // If this view is GONE, then skip it -- keep the current + // frame, and let the caller know so they can ignore it + // if they want. (We do the normal layout for INVISIBLE + // windows, since that means "perform layout as normal, + // just don't display"). + if (attachedBehindDream && mPolicy.canBeForceHidden(win, win.mAttrs)) { + continue; + } + if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) + || !win.mHaveFrame || win.mLayoutNeeded) { + if (initial) { + //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); + win.mContentChanged = false; + } + win.mLayoutNeeded = false; + win.prelayout(); + mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + win.mLayoutSeq = seq; + if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + + win.mFrame + " mContainingFrame=" + + win.mContainingFrame + " mDisplayFrame=" + + win.mDisplayFrame); + } + } else if (win.mAttrs.type == TYPE_DREAM) { + // Don't layout windows behind a dream, so that if it + // does stuff like hide the status bar we won't get a + // bad transition when it goes away. + attachedBehindDream = behindDream; + } + } + + // Window frames may have changed. Tell the input dispatcher about it. + mInputMonitor.setUpdateInputWindowsNeededLw(); + if (updateInputWindows) { + mInputMonitor.updateInputWindowsLw(false /*force*/); + } + + mPolicy.finishLayoutLw(); + } + + void makeWindowFreezingScreenIfNeededLocked(WindowState w) { + // If the screen is currently frozen or off, then keep + // it frozen/off until this window draws at its new + // orientation. + if (!okToDisplay()) { + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Changing surface while display frozen: " + w); + w.mOrientationChanging = true; + w.mLastFreezeDuration = 0; + mInnerFields.mOrientationChangeComplete = false; + if (!mWindowsFreezingScreen) { + mWindowsFreezingScreen = true; + // XXX should probably keep timeout from + // when we first froze the display. + mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); + mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, + WINDOW_FREEZE_TIMEOUT_DURATION); + } + } + } + + /** + * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method. + * @param windows List of windows on default display. + * @return bitmap indicating if another pass through layout must be made. + */ + public int handleAppTransitionReadyLocked(WindowList windows) { + int changes = 0; + int i; + int NN = mOpeningApps.size(); + boolean goodToGo = true; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Checking " + NN + " opening apps (frozen=" + + mDisplayFrozen + " timeout=" + + mAppTransition.isTimeout() + ")..."); + if (!mDisplayFrozen && !mAppTransition.isTimeout()) { + // If the display isn't frozen, wait to do anything until + // all of the apps are ready. Otherwise just go because + // we'll unfreeze the display when everyone is ready. + for (i=0; i<NN && goodToGo; i++) { + AppWindowToken wtoken = mOpeningApps.get(i); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Check opening app=" + wtoken + ": allDrawn=" + + wtoken.allDrawn + " startingDisplayed=" + + wtoken.startingDisplayed + " startingMoved=" + + wtoken.startingMoved); + if (!wtoken.allDrawn && !wtoken.startingDisplayed + && !wtoken.startingMoved) { + goodToGo = false; + } + } + } + if (goodToGo) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); + int transit = mAppTransition.getAppTransition(); + if (mSkipAppTransitionAnimation) { + transit = AppTransition.TRANSIT_UNSET; + } + mAppTransition.goodToGo(); + mStartingIconInTransition = false; + mSkipAppTransitionAnimation = false; + + mH.removeMessages(H.APP_TRANSITION_TIMEOUT); + + rebuildAppWindowListLocked(); + + // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper + WindowState oldWallpaper = + mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimating() + && !mWallpaperTarget.mWinAnimator.isDummyAnimation() + ? null : mWallpaperTarget; + + mInnerFields.mWallpaperMayChange = false; + + // The top-most window will supply the layout params, + // and we will determine it below. + LayoutParams animLp = null; + int bestAnimLayer = -1; + boolean fullscreenAnim = false; + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New wallpaper target=" + mWallpaperTarget + + ", oldWallpaper=" + oldWallpaper + + ", lower target=" + mLowerWallpaperTarget + + ", upper target=" + mUpperWallpaperTarget); + + boolean openingAppHasWallpaper = false; + boolean closingAppHasWallpaper = false; + final AppWindowToken lowerWallpaperAppToken; + final AppWindowToken upperWallpaperAppToken; + if (mLowerWallpaperTarget == null) { + lowerWallpaperAppToken = upperWallpaperAppToken = null; + } else { + lowerWallpaperAppToken = mLowerWallpaperTarget.mAppToken; + upperWallpaperAppToken = mUpperWallpaperTarget.mAppToken; + } + + // Do a first pass through the tokens for two + // things: + // (1) Determine if both the closing and opening + // app token sets are wallpaper targets, in which + // case special animations are needed + // (since the wallpaper needs to stay static + // behind them). + // (2) Find the layout params of the top-most + // application window in the tokens, which is + // what will control the animation theme. + final int NC = mClosingApps.size(); + NN = NC + mOpeningApps.size(); + for (i=0; i<NN; i++) { + final AppWindowToken wtoken; + if (i < NC) { + wtoken = mClosingApps.get(i); + if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { + closingAppHasWallpaper = true; + } + } else { + wtoken = mOpeningApps.get(i - NC); + if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { + openingAppHasWallpaper = true; + } + } + + if (wtoken.appFullscreen) { + WindowState ws = wtoken.findMainWindow(); + if (ws != null) { + animLp = ws.mAttrs; + bestAnimLayer = ws.mLayer; + fullscreenAnim = true; + } + } else if (!fullscreenAnim) { + WindowState ws = wtoken.findMainWindow(); + if (ws != null) { + if (ws.mLayer > bestAnimLayer) { + animLp = ws.mAttrs; + bestAnimLayer = ws.mLayer; + } + } + } + } + + mAnimateWallpaperWithTarget = false; + if (closingAppHasWallpaper && openingAppHasWallpaper) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); + switch (transit) { + case AppTransition.TRANSIT_ACTIVITY_OPEN: + case AppTransition.TRANSIT_TASK_OPEN: + case AppTransition.TRANSIT_TASK_TO_FRONT: + transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; + break; + case AppTransition.TRANSIT_ACTIVITY_CLOSE: + case AppTransition.TRANSIT_TASK_CLOSE: + case AppTransition.TRANSIT_TASK_TO_BACK: + transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; + break; + } + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit: " + transit); + } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty() + && !mOpeningApps.contains(oldWallpaper.mAppToken)) { + // We are transitioning from an activity with + // a wallpaper to one without. + transit = AppTransition.TRANSIT_WALLPAPER_CLOSE; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit away from wallpaper: " + transit); + } else if (mWallpaperTarget != null && mWallpaperTarget.isVisibleLw()) { + // We are transitioning from an activity without + // a wallpaper to now showing the wallpaper + transit = AppTransition.TRANSIT_WALLPAPER_OPEN; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit into wallpaper: " + transit); + } else { + mAnimateWallpaperWithTarget = true; + } + + // If all closing windows are obscured, then there is + // no need to do an animation. This is the case, for + // example, when this transition is being done behind + // the lock screen. + if (!mPolicy.allowAppAnimationsLw()) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Animations disallowed by keyguard or dream."); + animLp = null; + } + + AppWindowToken topOpeningApp = null; + int topOpeningLayer = 0; + + NN = mOpeningApps.size(); + for (i=0; i<NN; i++) { + AppWindowToken wtoken = mOpeningApps.get(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + appAnimator.clearThumbnail(); + wtoken.inPendingTransaction = false; + appAnimator.animation = null; + setTokenVisibilityLocked(wtoken, animLp, true, transit, false); + wtoken.updateReportedVisibilityLocked(); + wtoken.waitingToShow = false; + + appAnimator.mAllAppWinAnimators.clear(); + final int N = wtoken.allAppWindows.size(); + for (int j = 0; j < N; j++) { + appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); + } + mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); + + if (animLp != null) { + int layer = -1; + for (int j=0; j<wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mWinAnimator.mAnimLayer > layer) { + layer = win.mWinAnimator.mAnimLayer; + } + } + if (topOpeningApp == null || layer > topOpeningLayer) { + topOpeningApp = wtoken; + topOpeningLayer = layer; + } + } + } + NN = mClosingApps.size(); + for (i=0; i<NN; i++) { + AppWindowToken wtoken = mClosingApps.get(i); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); + wtoken.mAppAnimator.clearThumbnail(); + wtoken.inPendingTransaction = false; + wtoken.mAppAnimator.animation = null; + setTokenVisibilityLocked(wtoken, animLp, false, transit, false); + wtoken.updateReportedVisibilityLocked(); + wtoken.waitingToHide = false; + // Force the allDrawn flag, because we want to start + // this guy's animations regardless of whether it's + // gotten drawn. + wtoken.allDrawn = true; + wtoken.deferClearAllDrawn = false; + } + + AppWindowAnimator appAnimator = + topOpeningApp == null ? null : topOpeningApp.mAppAnimator; + Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail(); + if (nextAppTransitionThumbnail != null && appAnimator != null + && appAnimator.animation != null) { + // This thumbnail animation is very special, we need to have + // an extra surface with the thumbnail included with the animation. + Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(), + nextAppTransitionThumbnail.getHeight()); + try { + // TODO(multi-display): support other displays + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final Display display = displayContent.getDisplay(); + SurfaceControl surfaceControl = new SurfaceControl(mFxSession, + "thumbnail anim", + dirty.width(), dirty.height(), + PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); + surfaceControl.setLayerStack(display.getLayerStack()); + appAnimator.thumbnail = surfaceControl; + if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE"); + Surface drawSurface = new Surface(); + drawSurface.copyFrom(surfaceControl); + Canvas c = drawSurface.lockCanvas(dirty); + c.drawBitmap(nextAppTransitionThumbnail, 0, 0, null); + drawSurface.unlockCanvasAndPost(c); + drawSurface.release(); + appAnimator.thumbnailLayer = topOpeningLayer; + DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); + Animation anim = mAppTransition.createThumbnailAnimationLocked( + transit, true, true, displayInfo.appWidth, displayInfo.appHeight); + appAnimator.thumbnailAnimation = anim; + anim.restrictDuration(MAX_ANIMATION_DURATION); + anim.scaleCurrentDuration(mTransitionAnimationScale); + Point p = new Point(); + mAppTransition.getStartingPoint(p); + appAnimator.thumbnailX = p.x; + appAnimator.thumbnailY = p.y; + } catch (OutOfResourcesException e) { + Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w=" + dirty.width() + + " h=" + dirty.height(), e); + appAnimator.clearThumbnail(); + } + } + + mAppTransition.postAnimationCallback(); + mAppTransition.clear(); + + mOpeningApps.clear(); + mClosingApps.clear(); + + // This has changed the visibility of windows, so perform + // a new layout to get them all up-to-date. + changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT + | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; + getDefaultDisplayContentLocked().layoutNeeded = true; + + // TODO(multidisplay): IMEs are only supported on the default display. + if (windows == getDefaultWindowListLocked() + && !moveInputMethodWindowsIfNeededLocked(true)) { + assignLayersLocked(windows); + } + updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/); + mFocusMayChange = false; + } + + return changes; + } + + /** + * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method. + * @return bitmap indicating if another pass through layout must be made. + */ + private int handleAnimatingStoppedAndTransitionLocked() { + int changes = 0; + + mAppTransition.setIdle(); + // Restore window app tokens to the ActivityManager views + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + wtoken.sendingToBottom = false; + } + } + rebuildAppWindowListLocked(); + + changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Wallpaper layer changed: assigning layers + relayout"); + moveInputMethodWindowsIfNeededLocked(true); + mInnerFields.mWallpaperMayChange = true; + // Since the window list has been rebuilt, focus might + // have to be recomputed since the actual order of windows + // might have changed again. + mFocusMayChange = true; + + return changes; + } + + private void updateResizingWindows(final WindowState w) { + final WindowStateAnimator winAnimator = w.mWinAnimator; + if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq) { + w.setInsetsChanged(); + boolean configChanged = w.isConfigChanged(); + if (DEBUG_CONFIGURATION && configChanged) { + Slog.v(TAG, "Win " + w + " config changed: " + + mCurConfiguration); + } + if (localLOGV) Slog.v(TAG, "Resizing " + w + + ": configChanged=" + configChanged + + " last=" + w.mLastFrame + " frame=" + w.mFrame); + w.mLastFrame.set(w.mFrame); + if (w.mContentInsetsChanged + || w.mVisibleInsetsChanged + || winAnimator.mSurfaceResized + || configChanged) { + if (DEBUG_RESIZE || DEBUG_ORIENTATION) { + Slog.v(TAG, "Resize reasons for w=" + w + ": " + + " contentInsetsChanged=" + w.mContentInsetsChanged + + " " + w.mContentInsets.toShortString() + + " visibleInsetsChanged=" + w.mVisibleInsetsChanged + + " " + w.mVisibleInsets.toShortString() + + " surfaceResized=" + winAnimator.mSurfaceResized + + " configChanged=" + configChanged); + } + + w.mLastOverscanInsets.set(w.mOverscanInsets); + w.mLastContentInsets.set(w.mContentInsets); + w.mLastVisibleInsets.set(w.mVisibleInsets); + makeWindowFreezingScreenIfNeededLocked(w); + // If the orientation is changing, then we need to + // hold off on unfreezing the display until this + // window has been redrawn; to do that, we need + // to go through the process of getting informed + // by the application when it has finished drawing. + if (w.mOrientationChanging) { + if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG, + "Orientation start waiting for draw mDrawState=DRAW_PENDING in " + + w + ", surface " + winAnimator.mSurfaceControl); + winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; + if (w.mAppToken != null) { + w.mAppToken.allDrawn = false; + w.mAppToken.deferClearAllDrawn = false; + } + } + if (!mResizingWindows.contains(w)) { + if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, + "Resizing window " + w + " to " + winAnimator.mSurfaceW + + "x" + winAnimator.mSurfaceH); + mResizingWindows.add(w); + } + } else if (w.mOrientationChanging) { + if (w.isDrawnLw()) { + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Orientation not waiting for draw in " + + w + ", surface " + winAnimator.mSurfaceControl); + w.mOrientationChanging = false; + w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); + } + } + } + } + + /** + * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method. + * + * @param w WindowState this method is applied to. + * @param currentTime The time which animations use for calculating transitions. + * @param innerDw Width of app window. + * @param innerDh Height of app window. + */ + private void handleNotObscuredLocked(final WindowState w, final long currentTime, + final int innerDw, final int innerDh) { + final WindowManager.LayoutParams attrs = w.mAttrs; + final int attrFlags = attrs.flags; + final boolean canBeSeen = w.isDisplayedLw(); + final boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn(); + + if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) { + // This window completely covers everything behind it, + // so we want to leave all of them as undimmed (for + // performance reasons). + mInnerFields.mObscured = true; + } + + if (w.mHasSurface) { + if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) { + mInnerFields.mHoldScreen = w.mSession; + } + if (!mInnerFields.mSyswin && w.mAttrs.screenBrightness >= 0 + && mInnerFields.mScreenBrightness < 0) { + mInnerFields.mScreenBrightness = w.mAttrs.screenBrightness; + } + if (!mInnerFields.mSyswin && w.mAttrs.buttonBrightness >= 0 + && mInnerFields.mButtonBrightness < 0) { + mInnerFields.mButtonBrightness = w.mAttrs.buttonBrightness; + } + if (!mInnerFields.mSyswin && w.mAttrs.userActivityTimeout >= 0 + && mInnerFields.mUserActivityTimeout < 0) { + mInnerFields.mUserActivityTimeout = w.mAttrs.userActivityTimeout; + } + + final int type = attrs.type; + if (canBeSeen + && (type == TYPE_SYSTEM_DIALOG + || type == TYPE_RECENTS_OVERLAY + || type == TYPE_KEYGUARD + || type == TYPE_SYSTEM_ERROR)) { + mInnerFields.mSyswin = true; + } + + if (canBeSeen) { + // This function assumes that the contents of the default display are + // processed first before secondary displays. + final DisplayContent displayContent = w.mDisplayContent; + if (displayContent.isDefaultDisplay) { + // While a dream or keyguard is showing, obscure ordinary application + // content on secondary displays (by forcibly enabling mirroring unless + // there is other content we want to show) but still allow opaque + // keyguard dialogs to be shown. + if (type == TYPE_DREAM || type == TYPE_KEYGUARD) { + mInnerFields.mObscureApplicationContentOnSecondaryDisplays = true; + } + mInnerFields.mDisplayHasContent = true; + } else if (!mInnerFields.mObscureApplicationContentOnSecondaryDisplays + || (mInnerFields.mObscured && type == TYPE_KEYGUARD_DIALOG)) { + // Allow full screen keyguard presentation dialogs to be seen. + mInnerFields.mDisplayHasContent = true; + } + } + } + } + + private void handleFlagDimBehind(WindowState w, int innerDw, int innerDh) { + final WindowManager.LayoutParams attrs = w.mAttrs; + if ((attrs.flags & FLAG_DIM_BEHIND) != 0 + && w.isDisplayedLw() + && !w.mExiting) { + final WindowStateAnimator winAnimator = w.mWinAnimator; + final TaskStack stack = w.getStack(); + stack.setDimmingTag(); + if (!stack.isDimming(winAnimator)) { + if (localLOGV) Slog.v(TAG, "Win " + w + " start dimming."); + stack.startDimmingIfNeeded(winAnimator); + } + } + } + + private void updateAllDrawnLocked(DisplayContent displayContent) { + // See if any windows have been drawn, so they (and others + // associated with them) can now be shown. + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + if (!wtoken.allDrawn) { + int numInteresting = wtoken.numInterestingWindows; + if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { + if (DEBUG_VISIBILITY) Slog.v(TAG, + "allDrawn: " + wtoken + + " interesting=" + numInteresting + + " drawn=" + wtoken.numDrawnWindows); + wtoken.allDrawn = true; + mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget(); + } + } + } + } + } + + // "Something has changed! Let's make it correct now." + private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) { + if (DEBUG_WINDOW_TRACE) { + Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by " + + Debug.getCallers(3)); + } + + final long currentTime = SystemClock.uptimeMillis(); + + int i; + + if (mFocusMayChange) { + mFocusMayChange = false; + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/); + } + + // Initialize state of exiting tokens. + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + for (i=displayContent.mExitingTokens.size()-1; i>=0; i--) { + displayContent.mExitingTokens.get(i).hasVisible = false; + } + + // Initialize state of exiting applications. + for (i=displayContent.mExitingAppTokens.size()-1; i>=0; i--) { + displayContent.mExitingAppTokens.get(i).hasVisible = false; + } + } + + mInnerFields.mHoldScreen = null; + mInnerFields.mScreenBrightness = -1; + mInnerFields.mButtonBrightness = -1; + mInnerFields.mUserActivityTimeout = -1; + mInnerFields.mObscureApplicationContentOnSecondaryDisplays = false; + + mTransactionSequence++; + + final DisplayContent defaultDisplay = getDefaultDisplayContentLocked(); + final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo(); + final int defaultDw = defaultInfo.logicalWidth; + final int defaultDh = defaultInfo.logicalHeight; + + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces"); + SurfaceControl.openTransaction(); + try { + + if (mWatermark != null) { + mWatermark.positionSurface(defaultDw, defaultDh); + } + if (mStrictModeFlash != null) { + mStrictModeFlash.positionSurface(defaultDw, defaultDh); + } + + boolean focusDisplayed = false; + + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + boolean updateAllDrawn = false; + WindowList windows = displayContent.getWindowList(); + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int displayId = displayContent.getDisplayId(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; + final int innerDw = displayInfo.appWidth; + final int innerDh = displayInfo.appHeight; + final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + + // Reset for each display. + mInnerFields.mDisplayHasContent = false; + + int repeats = 0; + do { + repeats++; + if (repeats > 6) { + Slog.w(TAG, "Animation repeat aborted after too many iterations"); + displayContent.layoutNeeded = false; + break; + } + + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner", + displayContent.pendingLayoutChanges); + + if ((displayContent.pendingLayoutChanges & + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0 && + (adjustWallpaperWindowsLocked() & + ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { + assignLayersLocked(windows); + displayContent.layoutNeeded = true; + } + + if (isDefaultDisplay && (displayContent.pendingLayoutChanges + & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { + if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout"); + if (updateOrientationFromAppTokensLocked(true)) { + displayContent.layoutNeeded = true; + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + + if ((displayContent.pendingLayoutChanges + & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) { + displayContent.layoutNeeded = true; + } + + // FIRST LOOP: Perform a layout, if needed. + if (repeats < 4) { + performLayoutLockedInner(displayContent, repeats == 1, + false /*updateInputWindows*/); + } else { + Slog.w(TAG, "Layout repeat skipped after too many iterations"); + } + + // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think + // it is animating. + displayContent.pendingLayoutChanges = 0; + + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + + mLayoutRepeatCount, displayContent.pendingLayoutChanges); + + if (isDefaultDisplay) { + mPolicy.beginPostLayoutPolicyLw(dw, dh); + for (i = windows.size() - 1; i >= 0; i--) { + WindowState w = windows.get(i); + if (w.mHasSurface) { + mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs); + } + } + displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw(); + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats( + "after finishPostLayoutPolicyLw", displayContent.pendingLayoutChanges); + } + } while (displayContent.pendingLayoutChanges != 0); + + mInnerFields.mObscured = false; + mInnerFields.mSyswin = false; + displayContent.resetDimming(); + + // Only used if default window + final boolean someoneLosingFocus = !mLosingFocus.isEmpty(); + + final int N = windows.size(); + for (i=N-1; i>=0; i--) { + WindowState w = windows.get(i); + + final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured; + + // Update effect. + w.mObscured = mInnerFields.mObscured; + if (!mInnerFields.mObscured) { + handleNotObscuredLocked(w, currentTime, innerDw, innerDh); + } + + if (!w.getStack().testDimmingTag()) { + handleFlagDimBehind(w, innerDw, innerDh); + } + + if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w) + && w.isVisibleLw()) { + // This is the wallpaper target and its obscured state + // changed... make sure the current wallaper's visibility + // has been updated accordingly. + updateWallpaperVisibilityLocked(); + } + + final WindowStateAnimator winAnimator = w.mWinAnimator; + + // If the window has moved due to its containing + // content frame changing, then we'd like to animate + // it. + if (w.mHasSurface && w.shouldAnimateMove()) { + // Frame has moved, containing content frame + // has also moved, and we're not currently animating... + // let's do something. + Animation a = AnimationUtils.loadAnimation(mContext, + com.android.internal.R.anim.window_move_from_decor); + winAnimator.setAnimation(a); + winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left; + winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top; + try { + w.mClient.moved(w.mFrame.left, w.mFrame.top); + } catch (RemoteException e) { + } + } + + //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing"); + w.mContentChanged = false; + + // Moved from updateWindowsAndWallpaperLocked(). + if (w.mHasSurface) { + // Take care of the window being ready to display. + final boolean committed = + winAnimator.commitFinishDrawingLocked(currentTime); + if (isDefaultDisplay && committed) { + if (w.mAttrs.type == TYPE_DREAM) { + // HACK: When a dream is shown, it may at that + // point hide the lock screen. So we need to + // redo the layout to let the phone window manager + // make this happen. + displayContent.pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; + if (DEBUG_LAYOUT_REPEATS) { + debugLayoutRepeats( + "dream and commitFinishDrawingLocked true", + displayContent.pendingLayoutChanges); + } + } + if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "First draw done in potential wallpaper target " + w); + mInnerFields.mWallpaperMayChange = true; + displayContent.pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + if (DEBUG_LAYOUT_REPEATS) { + debugLayoutRepeats( + "wallpaper and commitFinishDrawingLocked true", + displayContent.pendingLayoutChanges); + } + } + } + + winAnimator.setSurfaceBoundariesLocked(recoveringMemory); + + final AppWindowToken atoken = w.mAppToken; + if (DEBUG_STARTING_WINDOW && atoken != null + && w == atoken.startingWindow) { + Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + + w.isOnScreen() + " allDrawn=" + atoken.allDrawn + + " freezingScreen=" + atoken.mAppAnimator.freezingScreen); + } + if (atoken != null + && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) { + if (atoken.lastTransactionSequence != mTransactionSequence) { + atoken.lastTransactionSequence = mTransactionSequence; + atoken.numInterestingWindows = atoken.numDrawnWindows = 0; + atoken.startingDisplayed = false; + } + if ((w.isOnScreen() || winAnimator.mAttrType == TYPE_BASE_APPLICATION) + && !w.mExiting && !w.mDestroying) { + if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) { + Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw() + + ", isAnimating=" + winAnimator.isAnimating()); + if (!w.isDrawnLw()) { + Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceControl + + " pv=" + w.mPolicyVisibility + + " mDrawState=" + winAnimator.mDrawState + + " ah=" + w.mAttachedHidden + + " th=" + atoken.hiddenRequested + + " a=" + winAnimator.mAnimating); + } + } + if (w != atoken.startingWindow) { + if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) { + atoken.numInterestingWindows++; + if (w.isDrawnLw()) { + atoken.numDrawnWindows++; + if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG, + "tokenMayBeDrawn: " + atoken + + " freezingScreen=" + atoken.mAppAnimator.freezingScreen + + " mAppFreezing=" + w.mAppFreezing); + updateAllDrawn = true; + } + } + } else if (w.isDrawnLw()) { + atoken.startingDisplayed = true; + } + } + } + } + + if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus) + && w.isDisplayedLw()) { + focusDisplayed = true; + } + + updateResizingWindows(w); + } + + mDisplayManagerService.setDisplayHasContent(displayId, + mInnerFields.mDisplayHasContent, + true /* inTraversal, must call performTraversalInTrans... below */); + + getDisplayContentLocked(displayId).stopDimmingIfNeeded(); + + if (updateAllDrawn) { + updateAllDrawnLocked(displayContent); + } + } + + if (focusDisplayed) { + mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS); + } + + // Give the display manager a chance to adjust properties + // like display rotation if it needs to. + mDisplayManagerService.performTraversalInTransactionFromWindowManager(); + + } catch (RuntimeException e) { + Log.wtf(TAG, "Unhandled exception in Window Manager", e); + } finally { + SurfaceControl.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); + } + + final WindowList defaultWindows = defaultDisplay.getWindowList(); + + // If we are ready to perform an app transition, check through + // all of the app tokens to be shown and see if they are ready + // to go. + if (mAppTransition.isReady()) { + defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows); + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked", + defaultDisplay.pendingLayoutChanges); + } + + if (!mAnimator.mAnimating && mAppTransition.isRunning()) { + // We have finished the animation of an app transition. To do + // this, we have delayed a lot of operations like showing and + // hiding apps, moving apps in Z-order, etc. The app token list + // reflects the correct Z-order, but the window list may now + // be out of sync with it. So here we will just rebuild the + // entire app window list. Fun! + defaultDisplay.pendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked(); + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock", + defaultDisplay.pendingLayoutChanges); + } + + if (mInnerFields.mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0 + && !mAppTransition.isReady()) { + // At this point, there was a window with a wallpaper that + // was force hiding other windows behind it, but now it + // is going away. This may be simple -- just animate + // away the wallpaper and its window -- or it may be + // hard -- the wallpaper now needs to be shown behind + // something that was hidden. + defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked", + defaultDisplay.pendingLayoutChanges); + } + mInnerFields.mWallpaperForceHidingChanged = false; + + if (mInnerFields.mWallpaperMayChange) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting"); + defaultDisplay.pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("WallpaperMayChange", + defaultDisplay.pendingLayoutChanges); + } + + if (mFocusMayChange) { + mFocusMayChange = false; + if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, + false /*updateInputWindows*/)) { + defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; + } + } + + if (needsLayout()) { + defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; + if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", + defaultDisplay.pendingLayoutChanges); + } + + for (i = mResizingWindows.size() - 1; i >= 0; i--) { + WindowState win = mResizingWindows.get(i); + if (win.mAppFreezing) { + // Don't remove this window until rotation has completed. + continue; + } + final WindowStateAnimator winAnimator = win.mWinAnimator; + try { + if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, + "Reporting new frame to " + win + ": " + win.mCompatFrame); + int diff = 0; + boolean configChanged = win.isConfigChanged(); + if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) + && configChanged) { + Slog.i(TAG, "Sending new config to window " + win + ": " + + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH + + " / " + mCurConfiguration + " / 0x" + + Integer.toHexString(diff)); + } + win.setConfiguration(mCurConfiguration); + if (DEBUG_ORIENTATION && + winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i( + TAG, "Resizing " + win + " WITH DRAW PENDING"); + final IWindow client = win.mClient; + final Rect frame = win.mFrame; + final Rect overscanInsets = win.mLastOverscanInsets; + final Rect contentInsets = win.mLastContentInsets; + final Rect visibleInsets = win.mLastVisibleInsets; + final boolean reportDraw + = winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING; + final Configuration newConfig = configChanged ? win.mConfiguration : null; + if (win.mClient instanceof IWindow.Stub) { + // To prevent deadlock simulate one-way call if win.mClient is a local object. + mH.post(new Runnable() { + @Override + public void run() { + try { + client.resized(frame, overscanInsets, contentInsets, + visibleInsets, reportDraw, newConfig); + } catch (RemoteException e) { + // Not a remote call, RemoteException won't be raised. + } + } + }); + } else { + client.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw, + newConfig); + } + win.mOverscanInsetsChanged = false; + win.mContentInsetsChanged = false; + win.mVisibleInsetsChanged = false; + winAnimator.mSurfaceResized = false; + } catch (RemoteException e) { + win.mOrientationChanging = false; + win.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); + } + mResizingWindows.remove(i); + } + + if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG, + "With display frozen, orientationChangeComplete=" + + mInnerFields.mOrientationChangeComplete); + if (mInnerFields.mOrientationChangeComplete) { + if (mWindowsFreezingScreen) { + mWindowsFreezingScreen = false; + mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource; + mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); + } + stopFreezingDisplayLocked(); + } + + // Destroy the surface of any windows that are no longer visible. + boolean wallpaperDestroyed = false; + i = mDestroySurface.size(); + if (i > 0) { + do { + i--; + WindowState win = mDestroySurface.get(i); + win.mDestroying = false; + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } + if (win == mWallpaperTarget) { + wallpaperDestroyed = true; + } + win.mWinAnimator.destroySurfaceLocked(); + } while (i > 0); + mDestroySurface.clear(); + } + + // Time to remove any exiting tokens? + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens; + for (i = exitingTokens.size() - 1; i >= 0; i--) { + WindowToken token = exitingTokens.get(i); + if (!token.hasVisible) { + exitingTokens.remove(i); + if (token.windowType == TYPE_WALLPAPER) { + mWallpaperTokens.remove(token); + } + } + } + + // Time to remove any exiting applications? + AppTokenList exitingAppTokens = displayContent.mExitingAppTokens; + for (i = exitingAppTokens.size() - 1; i >= 0; i--) { + AppWindowToken token = exitingAppTokens.get(i); + if (!token.hasVisible && !mClosingApps.contains(token)) { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + token.mAppAnimator.clearAnimation(); + token.mAppAnimator.animating = false; + if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, + "performLayout: App token exiting now removed" + token); + final Task task = mTaskIdToTask.get(token.groupId); + if (task != null && task.removeAppToken(token)) { + mTaskIdToTask.delete(token.groupId); + } + exitingAppTokens.remove(i); + } + } + } + + if (!mAnimator.mAnimating && mRelayoutWhileAnimating.size() > 0) { + for (int j=mRelayoutWhileAnimating.size()-1; j>=0; j--) { + try { + mRelayoutWhileAnimating.get(j).mClient.doneAnimating(); + } catch (RemoteException e) { + } + } + mRelayoutWhileAnimating.clear(); + } + + if (wallpaperDestroyed) { + defaultDisplay.pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + defaultDisplay.layoutNeeded = true; + } + + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + if (displayContent.pendingLayoutChanges != 0) { + displayContent.layoutNeeded = true; + } + } + + // Finally update all input windows now that the window changes have stabilized. + mInputMonitor.updateInputWindowsLw(true /*force*/); + + setHoldScreenLocked(mInnerFields.mHoldScreen); + if (!mDisplayFrozen) { + if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) { + mPowerManager.setScreenBrightnessOverrideFromWindowManager(-1); + } else { + mPowerManager.setScreenBrightnessOverrideFromWindowManager( + toBrightnessOverride(mInnerFields.mScreenBrightness)); + } + if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) { + mPowerManager.setButtonBrightnessOverrideFromWindowManager(-1); + } else { + mPowerManager.setButtonBrightnessOverrideFromWindowManager( + toBrightnessOverride(mInnerFields.mButtonBrightness)); + } + mPowerManager.setUserActivityTimeoutOverrideFromWindowManager( + mInnerFields.mUserActivityTimeout); + } + + if (mTurnOnScreen) { + if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!"); + mPowerManager.wakeUp(SystemClock.uptimeMillis()); + mTurnOnScreen = false; + } + + if (mInnerFields.mUpdateRotation) { + if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation"); + if (updateRotationUncheckedLocked(false)) { + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } else { + mInnerFields.mUpdateRotation = false; + } + } + + if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded + && !mInnerFields.mUpdateRotation) { + checkDrawnWindowsLocked(); + } + + final int N = mPendingRemove.size(); + if (N > 0) { + if (mPendingRemoveTmp.length < N) { + mPendingRemoveTmp = new WindowState[N+10]; + } + mPendingRemove.toArray(mPendingRemoveTmp); + mPendingRemove.clear(); + DisplayContentList displayList = new DisplayContentList(); + for (i = 0; i < N; i++) { + WindowState w = mPendingRemoveTmp[i]; + removeWindowInnerLocked(w.mSession, w); + if (!displayList.contains(w.mDisplayContent)) { + displayList.add(w.mDisplayContent); + } + } + + for (DisplayContent displayContent : displayList) { + assignLayersLocked(displayContent.getWindowList()); + displayContent.layoutNeeded = true; + } + } + + setFocusedStackFrame(); + + // Check to see if we are now in a state where the screen should + // be enabled, because the window obscured flags have changed. + enableScreenIfNeededLocked(); + + scheduleAnimationLocked(); + + if (DEBUG_WINDOW_TRACE) { + Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating=" + + mAnimator.mAnimating); + } + } + + private int toBrightnessOverride(float value) { + return (int)(value * PowerManager.BRIGHTNESS_ON); + } + + void checkDrawnWindowsLocked() { + if (mWaitingForDrawn.size() > 0) { + for (int j=mWaitingForDrawn.size()-1; j>=0; j--) { + Pair<WindowState, IRemoteCallback> pair = mWaitingForDrawn.get(j); + WindowState win = pair.first; + //Slog.i(TAG, "Waiting for drawn " + win + ": removed=" + // + win.mRemoved + " visible=" + win.isVisibleLw() + // + " shown=" + win.mSurfaceShown); + if (win.mRemoved) { + // Window has been removed; no draw will now happen, so stop waiting. + Slog.w(TAG, "Aborted waiting for drawn: " + pair.first); + try { + pair.second.sendResult(null); + } catch (RemoteException e) { + } + mWaitingForDrawn.remove(pair); + mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair); + } else if (win.mWinAnimator.mSurfaceShown) { + // Window is now drawn (and shown). + try { + pair.second.sendResult(null); + } catch (RemoteException e) { + } + mWaitingForDrawn.remove(pair); + mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair); + } + } + } + } + + @Override + public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) { + if (token != null && callback != null) { + synchronized (mWindowMap) { + WindowState win = windowForClientLocked(null, token, true); + if (win != null) { + Pair<WindowState, IRemoteCallback> pair = + new Pair<WindowState, IRemoteCallback>(win, callback); + Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair); + mH.sendMessageDelayed(m, 2000); + mWaitingForDrawn.add(pair); + checkDrawnWindowsLocked(); + return true; + } + Slog.i(TAG, "waitForWindowDrawn: win null"); + } + } + return false; + } + + void setHoldScreenLocked(final Session newHoldScreen) { + final boolean hold = newHoldScreen != null; + + if (hold && mHoldingScreenOn != newHoldScreen) { + mHoldingScreenWakeLock.setWorkSource(new WorkSource(newHoldScreen.mUid)); + } + mHoldingScreenOn = newHoldScreen; + + final boolean state = mHoldingScreenWakeLock.isHeld(); + if (hold != state) { + if (hold) { + mHoldingScreenWakeLock.acquire(); + mPolicy.keepScreenOnStartedLw(); + } else { + mPolicy.keepScreenOnStoppedLw(); + mHoldingScreenWakeLock.release(); + } + } + } + + @Override + public void requestTraversal() { + synchronized (mWindowMap) { + requestTraversalLocked(); + } + } + + void requestTraversalLocked() { + if (!mTraversalScheduled) { + mTraversalScheduled = true; + mH.sendEmptyMessage(H.DO_TRAVERSAL); + } + } + + /** Note that Locked in this case is on mLayoutToAnim */ + void scheduleAnimationLocked() { + if (!mAnimationScheduled) { + mAnimationScheduled = true; + mChoreographer.postCallback( + Choreographer.CALLBACK_ANIMATION, mAnimator.mAnimationRunnable, null); + } + } + + private boolean needsLayout() { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + if (displayContent.layoutNeeded) { + return true; + } + } + return false; + } + + boolean copyAnimToLayoutParamsLocked() { + boolean doRequest = false; + + final int bulkUpdateParams = mAnimator.mBulkUpdateParams; + if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) { + mInnerFields.mUpdateRotation = true; + doRequest = true; + } + if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) { + mInnerFields.mWallpaperMayChange = true; + doRequest = true; + } + if ((bulkUpdateParams & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) { + mInnerFields.mWallpaperForceHidingChanged = true; + doRequest = true; + } + if ((bulkUpdateParams & LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE) == 0) { + mInnerFields.mOrientationChangeComplete = false; + } else { + mInnerFields.mOrientationChangeComplete = true; + mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource; + if (mWindowsFreezingScreen) { + doRequest = true; + } + } + if ((bulkUpdateParams & LayoutFields.SET_TURN_ON_SCREEN) != 0) { + mTurnOnScreen = true; + } + if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_ACTION_PENDING) != 0) { + mInnerFields.mWallpaperActionPending = true; + } + + return doRequest; + } + + /** If a window that has an animation specifying a colored background and the current wallpaper + * is visible, then the color goes *below* the wallpaper so we don't cause the wallpaper to + * suddenly disappear. */ + int adjustAnimationBackground(WindowStateAnimator winAnimator) { + WindowList windows = winAnimator.mWin.getWindowList(); + for (int i = windows.size() - 1; i >= 0; --i) { + WindowState testWin = windows.get(i); + if (testWin.mIsWallpaper && testWin.isVisibleNow()) { + return testWin.mWinAnimator.mAnimLayer; + } + } + return winAnimator.mAnimLayer; + } + + boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation, + boolean secure) { + final SurfaceControl surface = winAnimator.mSurfaceControl; + boolean leakedSurface = false; + boolean killedApps = false; + + EventLog.writeEvent(EventLogTags.WM_NO_SURFACE_MEMORY, winAnimator.mWin.toString(), + winAnimator.mSession.mPid, operation); + + if (mForceRemoves == null) { + mForceRemoves = new ArrayList<WindowState>(); + } + + long callingIdentity = Binder.clearCallingIdentity(); + try { + // There was some problem... first, do a sanity check of the + // window list to make sure we haven't left any dangling surfaces + // around. + + Slog.i(TAG, "Out of memory for surface! Looking for leaks..."); + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState ws = windows.get(winNdx); + WindowStateAnimator wsa = ws.mWinAnimator; + if (wsa.mSurfaceControl != null) { + if (!mSessions.contains(wsa.mSession)) { + Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): " + + ws + " surface=" + wsa.mSurfaceControl + + " token=" + ws.mToken + + " pid=" + ws.mSession.mPid + + " uid=" + ws.mSession.mUid); + if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null); + wsa.mSurfaceControl.destroy(); + wsa.mSurfaceShown = false; + wsa.mSurfaceControl = null; + ws.mHasSurface = false; + mForceRemoves.add(ws); + leakedSurface = true; + } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) { + Slog.w(TAG, "LEAKED SURFACE (app token hidden): " + + ws + " surface=" + wsa.mSurfaceControl + + " token=" + ws.mAppToken); + if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null); + wsa.mSurfaceControl.destroy(); + wsa.mSurfaceShown = false; + wsa.mSurfaceControl = null; + ws.mHasSurface = false; + leakedSurface = true; + } + } + } + } + + if (!leakedSurface) { + Slog.w(TAG, "No leaked surfaces; killing applicatons!"); + SparseIntArray pidCandidates = new SparseIntArray(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + final int numWindows = windows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState ws = windows.get(winNdx); + if (mForceRemoves.contains(ws)) { + continue; + } + WindowStateAnimator wsa = ws.mWinAnimator; + if (wsa.mSurfaceControl != null) { + pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid); + } + } + if (pidCandidates.size() > 0) { + int[] pids = new int[pidCandidates.size()]; + for (int i=0; i<pids.length; i++) { + pids[i] = pidCandidates.keyAt(i); + } + try { + if (mActivityManager.killPids(pids, "Free memory", secure)) { + killedApps = true; + } + } catch (RemoteException e) { + } + } + } + } + + if (leakedSurface || killedApps) { + // We managed to reclaim some memory, so get rid of the trouble + // surface and ask the app to request another one. + Slog.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry."); + if (surface != null) { + if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin, + "RECOVER DESTROY", null); + surface.destroy(); + winAnimator.mSurfaceShown = false; + winAnimator.mSurfaceControl = null; + winAnimator.mWin.mHasSurface = false; + scheduleRemoveStartingWindow(winAnimator.mWin.mAppToken); + } + + try { + winAnimator.mWin.mClient.dispatchGetNewSurface(); + } catch (RemoteException e) { + } + } + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + + return leakedSurface || killedApps; + } + + private boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { + WindowState newFocus = computeFocusedWindowLocked(); + if (mCurrentFocus != newFocus) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus"); + // This check makes sure that we don't already have the focus + // change message pending. + mH.removeMessages(H.REPORT_FOCUS_CHANGE); + mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); + // TODO(multidisplay): Focused windows on default display only. + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final boolean imWindowChanged = moveInputMethodWindowsIfNeededLocked( + mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS + && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES); + if (imWindowChanged) { + displayContent.layoutNeeded = true; + newFocus = computeFocusedWindowLocked(); + } + + if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG, "Changing focus from " + + mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4)); + final WindowState oldFocus = mCurrentFocus; + mCurrentFocus = newFocus; + mLosingFocus.remove(newFocus); + int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus); + + if (imWindowChanged && oldFocus != mInputMethodWindow) { + // Focus of the input method window changed. Perform layout if needed. + if (mode == UPDATE_FOCUS_PLACING_SURFACES) { + performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows); + focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; + } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) { + // Client will do the layout, but we need to assign layers + // for handleNewWindowLocked() below. + assignLayersLocked(displayContent.getWindowList()); + } + } + + if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) { + // The change in focus caused us to need to do a layout. Okay. + displayContent.layoutNeeded = true; + if (mode == UPDATE_FOCUS_PLACING_SURFACES) { + performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows); + } + } + + if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) { + // If we defer assigning layers, then the caller is responsible for + // doing this part. + finishUpdateFocusedWindowAfterAssignLayersLocked(updateInputWindows); + } + + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + return true; + } + return false; + } + + private void finishUpdateFocusedWindowAfterAssignLayersLocked(boolean updateInputWindows) { + mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows); + } + + private WindowState computeFocusedWindowLocked() { + if (mAnimator.mUniverseBackground != null + && mAnimator.mUniverseBackground.mWin.canReceiveKeys()) { + return mAnimator.mUniverseBackground.mWin; + } + + final int displayCount = mDisplayContents.size(); + for (int i = 0; i < displayCount; i++) { + final DisplayContent displayContent = mDisplayContents.valueAt(i); + WindowState win = findFocusedWindowLocked(displayContent); + if (win != null) { + return win; + } + } + return null; + } + + private WindowState findFocusedWindowLocked(DisplayContent displayContent) { + final WindowList windows = displayContent.getWindowList(); + for (int i = windows.size() - 1; i >= 0; i--) { + final WindowState win = windows.get(i); + + if (localLOGV || DEBUG_FOCUS) Slog.v( + TAG, "Looking for focus: " + i + + " = " + win + + ", flags=" + win.mAttrs.flags + + ", canReceive=" + win.canReceiveKeys()); + + AppWindowToken wtoken = win.mAppToken; + + // If this window's application has been removed, just skip it. + if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) { + if (DEBUG_FOCUS) Slog.v(TAG, "Skipping " + wtoken + " because " + + (wtoken.removed ? "removed" : "sendingToBottom")); + continue; + } + + if (!win.canReceiveKeys()) { + continue; + } + + // Descend through all of the app tokens and find the first that either matches + // win.mAppToken (return win) or mFocusedApp (return null). + if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING && + mFocusedApp != null) { + ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + int tokenNdx = tokens.size() - 1; + for ( ; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken token = tokens.get(tokenNdx); + if (wtoken == token) { + break; + } + if (mFocusedApp == token) { + // Whoops, we are below the focused app... no focus for you! + if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG, + "findFocusedWindow: Reached focused app=" + mFocusedApp); + return null; + } + } + if (tokenNdx >= 0) { + // Early exit from loop, must have found the matching token. + break; + } + } + } + + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: Found new focus @ " + i + + " = " + win); + return win; + } + + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: No focusable windows."); + return null; + } + + private void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) { + if (mDisplayFrozen) { + return; + } + + if (!mDisplayReady || !mPolicy.isScreenOnFully()) { + // No need to freeze the screen before the system is ready or if + // the screen is off. + return; + } + + mScreenFrozenLock.acquire(); + + mDisplayFrozen = true; + mDisplayFreezeTime = SystemClock.elapsedRealtime(); + mLastFinishedFreezeSource = null; + + mInputMonitor.freezeInputDispatchingLw(); + + // Clear the last input window -- that is just used for + // clean transitions between IMEs, and if we are freezing + // the screen then the whole world is changing behind the scenes. + mPolicy.setLastInputMethodWindowLw(null, null); + + if (mAppTransition.isTransitionSet()) { + mAppTransition.freeze(); + } + + if (PROFILE_ORIENTATION) { + File file = new File("/data/system/frozen"); + Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); + } + + if (CUSTOM_SCREEN_ROTATION) { + mExitAnimId = exitAnim; + mEnterAnimId = enterAnim; + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final int displayId = displayContent.getDisplayId(); + ScreenRotationAnimation screenRotationAnimation = + mAnimator.getScreenRotationAnimationLocked(displayId); + if (screenRotationAnimation != null) { + screenRotationAnimation.kill(); + } + + // TODO(multidisplay): rotation on main screen only. + screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent, + mFxSession, inTransaction, mPolicy.isDefaultOrientationForced()); + mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation); + } + } + + private void stopFreezingDisplayLocked() { + if (!mDisplayFrozen) { + return; + } + + if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen + || mClientFreezingScreen) { + if (DEBUG_ORIENTATION) Slog.d(TAG, + "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig + + ", mAppsFreezingScreen=" + mAppsFreezingScreen + + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen + + ", mClientFreezingScreen=" + mClientFreezingScreen); + return; + } + + mDisplayFrozen = false; + mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime); + StringBuilder sb = new StringBuilder(128); + sb.append("Screen frozen for "); + TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb); + if (mLastFinishedFreezeSource != null) { + sb.append(" due to "); + sb.append(mLastFinishedFreezeSource); + } + Slog.i(TAG, sb.toString()); + mH.removeMessages(H.APP_FREEZE_TIMEOUT); + mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); + if (PROFILE_ORIENTATION) { + Debug.stopMethodTracing(); + } + + boolean updateRotation = false; + + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final int displayId = displayContent.getDisplayId(); + ScreenRotationAnimation screenRotationAnimation = + mAnimator.getScreenRotationAnimationLocked(displayId); + if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null + && screenRotationAnimation.hasScreenshot()) { + if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation"); + // TODO(multidisplay): rotation on main screen only. + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + // Get rotation animation again, with new top window + boolean isDimming = displayContent.isDimming(); + if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) { + mExitAnimId = mEnterAnimId = 0; + } + if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION, + mTransitionAnimationScale, displayInfo.logicalWidth, + displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) { + scheduleAnimationLocked(); + } else { + screenRotationAnimation.kill(); + screenRotationAnimation = null; + mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation); + updateRotation = true; + } + } else { + if (screenRotationAnimation != null) { + screenRotationAnimation.kill(); + screenRotationAnimation = null; + mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation); + } + updateRotation = true; + } + + mInputMonitor.thawInputDispatchingLw(); + + boolean configChanged; + + // While the display is frozen we don't re-compute the orientation + // to avoid inconsistent states. However, something interesting + // could have actually changed during that time so re-evaluate it + // now to catch that. + configChanged = updateOrientationFromAppTokensLocked(false); + + // A little kludge: a lot could have happened while the + // display was frozen, so now that we are coming back we + // do a gc so that any remote references the system + // processes holds on others can be released if they are + // no longer needed. + mH.removeMessages(H.FORCE_GC); + mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000); + + mScreenFrozenLock.release(); + + if (updateRotation) { + if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation"); + configChanged |= updateRotationUncheckedLocked(false); + } + + if (configChanged) { + mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + + static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps, + DisplayMetrics dm) { + if (index < tokens.length) { + String str = tokens[index]; + if (str != null && str.length() > 0) { + try { + int val = Integer.parseInt(str); + return val; + } catch (Exception e) { + } + } + } + if (defUnits == TypedValue.COMPLEX_UNIT_PX) { + return defDps; + } + int val = (int)TypedValue.applyDimension(defUnits, defDps, dm); + return val; + } + + void createWatermarkInTransaction() { + if (mWatermark != null) { + return; + } + + File file = new File("/system/etc/setup.conf"); + FileInputStream in = null; + DataInputStream ind = null; + try { + in = new FileInputStream(file); + ind = new DataInputStream(in); + String line = ind.readLine(); + if (line != null) { + String[] toks = line.split("%"); + if (toks != null && toks.length > 0) { + mWatermark = new Watermark(getDefaultDisplayContentLocked().getDisplay(), + mRealDisplayMetrics, mFxSession, toks); + } + } + } catch (FileNotFoundException e) { + } catch (IOException e) { + } finally { + if (ind != null) { + try { + ind.close(); + } catch (IOException e) { + } + } else if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + } + + @Override + public void statusBarVisibilityChanged(int visibility) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + android.Manifest.permission.STATUS_BAR); + } + + synchronized (mWindowMap) { + mLastStatusBarVisibility = visibility; + visibility = mPolicy.adjustSystemUiVisibilityLw(visibility); + updateStatusBarVisibilityLocked(visibility); + } + } + + // TOOD(multidisplay): StatusBar on multiple screens? + void updateStatusBarVisibilityLocked(int visibility) { + mInputManager.setSystemUiVisibility(visibility); + final WindowList windows = getDefaultWindowListLocked(); + final int N = windows.size(); + for (int i = 0; i < N; i++) { + WindowState ws = windows.get(i); + try { + int curValue = ws.mSystemUiVisibility; + int diff = curValue ^ visibility; + // We are only interested in differences of one of the + // clearable flags... + diff &= View.SYSTEM_UI_CLEARABLE_FLAGS; + // ...if it has actually been cleared. + diff &= ~visibility; + int newValue = (curValue&~diff) | (visibility&diff); + if (newValue != curValue) { + ws.mSeq++; + ws.mSystemUiVisibility = newValue; + } + if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) { + ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq, + visibility, newValue, diff); + } + } catch (RemoteException e) { + // so sorry + } + } + } + + @Override + public void reevaluateStatusBarVisibility() { + synchronized (mWindowMap) { + int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility); + updateStatusBarVisibilityLocked(visibility); + performLayoutAndPlaceSurfacesLocked(); + } + } + + @Override + public FakeWindow addFakeWindow(Looper looper, + InputEventReceiver.Factory inputEventReceiverFactory, + String name, int windowType, int layoutParamsFlags, int layoutParamsPrivateFlags, + boolean canReceiveKeys, boolean hasFocus, boolean touchFullscreen) { + synchronized (mWindowMap) { + FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputEventReceiverFactory, + name, windowType, + layoutParamsFlags, layoutParamsPrivateFlags, canReceiveKeys, + hasFocus, touchFullscreen); + int i=0; + while (i<mFakeWindows.size()) { + if (mFakeWindows.get(i).mWindowLayer <= fw.mWindowLayer) { + break; + } + } + mFakeWindows.add(i, fw); + mInputMonitor.updateInputWindowsLw(true); + return fw; + } + } + + boolean removeFakeWindowLocked(FakeWindow window) { + synchronized (mWindowMap) { + if (mFakeWindows.remove(window)) { + mInputMonitor.updateInputWindowsLw(true); + return true; + } + return false; + } + } + + // It is assumed that this method is called only by InputMethodManagerService. + public void saveLastInputMethodWindowForTransition() { + synchronized (mWindowMap) { + // TODO(multidisplay): Pass in the displayID. + DisplayContent displayContent = getDefaultDisplayContentLocked(); + if (mInputMethodWindow != null) { + mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget); + } + } + } + + @Override + public boolean hasNavigationBar() { + return mPolicy.hasNavigationBar(); + } + + @Override + public void lockNow(Bundle options) { + mPolicy.lockNow(options); + } + + @Override + public boolean isSafeModeEnabled() { + return mSafeMode; + } + + void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { + pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); + mPolicy.dump(" ", pw, args); + } + + void dumpAnimatorLocked(PrintWriter pw, String[] args, boolean dumpAll) { + pw.println("WINDOW MANAGER ANIMATOR STATE (dumpsys window animator)"); + mAnimator.dumpLocked(pw, " ", dumpAll); + } + + void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { + pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); + if (mTokenMap.size() > 0) { + pw.println(" All tokens:"); + Iterator<WindowToken> it = mTokenMap.values().iterator(); + while (it.hasNext()) { + WindowToken token = it.next(); + pw.print(" "); pw.print(token); + if (dumpAll) { + pw.println(':'); + token.dump(pw, " "); + } else { + pw.println(); + } + } + } + if (mWallpaperTokens.size() > 0) { + pw.println(); + pw.println(" Wallpaper tokens:"); + for (int i=mWallpaperTokens.size()-1; i>=0; i--) { + WindowToken token = mWallpaperTokens.get(i); + pw.print(" Wallpaper #"); pw.print(i); + pw.print(' '); pw.print(token); + if (dumpAll) { + pw.println(':'); + token.dump(pw, " "); + } else { + pw.println(); + } + } + } + if (mFinishedStarting.size() > 0) { + pw.println(); + pw.println(" Finishing start of application tokens:"); + for (int i=mFinishedStarting.size()-1; i>=0; i--) { + WindowToken token = mFinishedStarting.get(i); + pw.print(" Finished Starting #"); pw.print(i); + pw.print(' '); pw.print(token); + if (dumpAll) { + pw.println(':'); + token.dump(pw, " "); + } else { + pw.println(); + } + } + } + if (mOpeningApps.size() > 0 || mClosingApps.size() > 0) { + pw.println(); + if (mOpeningApps.size() > 0) { + pw.print(" mOpeningApps="); pw.println(mOpeningApps); + } + if (mClosingApps.size() > 0) { + pw.print(" mClosingApps="); pw.println(mClosingApps); + } + } + } + + void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) { + pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)"); + if (mSessions.size() > 0) { + Iterator<Session> it = mSessions.iterator(); + while (it.hasNext()) { + Session s = it.next(); + pw.print(" Session "); pw.print(s); pw.println(':'); + s.dump(pw, " "); + } + } + } + + void dumpDisplayContentsLocked(PrintWriter pw, boolean dumpAll) { + pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)"); + if (mDisplayReady) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + displayContent.dump(" ", pw); + } + } else { + pw.println(" NO DISPLAY"); + } + } + + void dumpWindowsLocked(PrintWriter pw, boolean dumpAll, + ArrayList<WindowState> windows) { + pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)"); + dumpWindowsNoHeaderLocked(pw, dumpAll, windows); + } + + void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll, + ArrayList<WindowState> windows) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windowList = mDisplayContents.valueAt(displayNdx).getWindowList(); + for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) { + final WindowState w = windowList.get(winNdx); + if (windows == null || windows.contains(w)) { + pw.print(" Window #"); pw.print(winNdx); pw.print(' '); + pw.print(w); pw.println(":"); + w.dump(pw, " ", dumpAll || windows != null); + } + } + } + if (mInputMethodDialogs.size() > 0) { + pw.println(); + pw.println(" Input method dialogs:"); + for (int i=mInputMethodDialogs.size()-1; i>=0; i--) { + WindowState w = mInputMethodDialogs.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" IM Dialog #"); pw.print(i); pw.print(": "); pw.println(w); + } + } + } + if (mPendingRemove.size() > 0) { + pw.println(); + pw.println(" Remove pending for:"); + for (int i=mPendingRemove.size()-1; i>=0; i--) { + WindowState w = mPendingRemove.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Remove #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } + if (mForceRemoves != null && mForceRemoves.size() > 0) { + pw.println(); + pw.println(" Windows force removing:"); + for (int i=mForceRemoves.size()-1; i>=0; i--) { + WindowState w = mForceRemoves.get(i); + pw.print(" Removing #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + if (mDestroySurface.size() > 0) { + pw.println(); + pw.println(" Windows waiting to destroy their surface:"); + for (int i=mDestroySurface.size()-1; i>=0; i--) { + WindowState w = mDestroySurface.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Destroy #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } + if (mLosingFocus.size() > 0) { + pw.println(); + pw.println(" Windows losing focus:"); + for (int i=mLosingFocus.size()-1; i>=0; i--) { + WindowState w = mLosingFocus.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Losing #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } + if (mResizingWindows.size() > 0) { + pw.println(); + pw.println(" Windows waiting to resize:"); + for (int i=mResizingWindows.size()-1; i>=0; i--) { + WindowState w = mResizingWindows.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Resizing #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } + if (mWaitingForDrawn.size() > 0) { + pw.println(); + pw.println(" Clients waiting for these windows to be drawn:"); + for (int i=mWaitingForDrawn.size()-1; i>=0; i--) { + Pair<WindowState, IRemoteCallback> pair = mWaitingForDrawn.get(i); + pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(pair.first); + pw.print(": "); pw.println(pair.second); + } + } + pw.println(); + pw.print(" mCurConfiguration="); pw.println(this.mCurConfiguration); + pw.print(" mCurrentFocus="); pw.println(mCurrentFocus); + if (mLastFocus != mCurrentFocus) { + pw.print(" mLastFocus="); pw.println(mLastFocus); + } + pw.print(" mFocusedApp="); pw.println(mFocusedApp); + if (mInputMethodTarget != null) { + pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget); + } + pw.print(" mInTouchMode="); pw.print(mInTouchMode); + pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + pw.print(" mLastDisplayFreezeDuration="); + TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); + if ( mLastFinishedFreezeSource != null) { + pw.print(" due to "); + pw.print(mLastFinishedFreezeSource); + } + pw.println(); + if (dumpAll) { + pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer); + pw.print(" mScreenRect="); pw.println(mScreenRect.toShortString()); + if (mLastStatusBarVisibility != 0) { + pw.print(" mLastStatusBarVisibility=0x"); + pw.println(Integer.toHexString(mLastStatusBarVisibility)); + } + if (mInputMethodWindow != null) { + pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow); + } + pw.print(" mWallpaperTarget="); pw.println(mWallpaperTarget); + if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) { + pw.print(" mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget); + pw.print(" mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget); + } + pw.print(" mLastWallpaperX="); pw.print(mLastWallpaperX); + pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY); + if (mInputMethodAnimLayerAdjustment != 0 || + mWallpaperAnimLayerAdjustment != 0) { + pw.print(" mInputMethodAnimLayerAdjustment="); + pw.print(mInputMethodAnimLayerAdjustment); + pw.print(" mWallpaperAnimLayerAdjustment="); + pw.println(mWallpaperAnimLayerAdjustment); + } + pw.print(" mSystemBooted="); pw.print(mSystemBooted); + pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled); + if (needsLayout()) { + pw.print(" layoutNeeded on displays="); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + if (displayContent.layoutNeeded) { + pw.print(displayContent.getDisplayId()); + } + } + pw.println(); + } + pw.print(" mTransactionSequence="); pw.println(mTransactionSequence); + pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen); + pw.print(" windows="); pw.print(mWindowsFreezingScreen); + pw.print(" client="); pw.print(mClientFreezingScreen); + pw.print(" apps="); pw.print(mAppsFreezingScreen); + pw.print(" waitingForConfig="); pw.println(mWaitingForConfig); + pw.print(" mRotation="); pw.print(mRotation); + pw.print(" mAltOrientation="); pw.println(mAltOrientation); + pw.print(" mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation); + pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation); + pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount); + pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale); + pw.print(" mTransitionWindowAnimationScale="); pw.print(mTransitionAnimationScale); + pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale); + pw.print(" mTraversalScheduled="); pw.println(mTraversalScheduled); + pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition); + pw.print(" mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation); + pw.println(" mLayoutToAnim:"); + mAppTransition.dump(pw); + } + } + + boolean dumpWindows(PrintWriter pw, String name, String[] args, + int opti, boolean dumpAll) { + WindowList windows = new WindowList(); + if ("visible".equals(name)) { + synchronized(mWindowMap) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windowList = + mDisplayContents.valueAt(displayNdx).getWindowList(); + for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) { + final WindowState w = windowList.get(winNdx); + if (w.mWinAnimator.mSurfaceShown) { + windows.add(w); + } + } + } + } + } else { + int objectId = 0; + // See if this is an object ID. + try { + objectId = Integer.parseInt(name, 16); + name = null; + } catch (RuntimeException e) { + } + synchronized(mWindowMap) { + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final WindowList windowList = + mDisplayContents.valueAt(displayNdx).getWindowList(); + for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) { + final WindowState w = windowList.get(winNdx); + if (name != null) { + if (w.mAttrs.getTitle().toString().contains(name)) { + windows.add(w); + } + } else if (System.identityHashCode(w) == objectId) { + windows.add(w); + } + } + } + } + } + + if (windows.size() <= 0) { + return false; + } + + synchronized(mWindowMap) { + dumpWindowsLocked(pw, dumpAll, windows); + } + return true; + } + + void dumpLastANRLocked(PrintWriter pw) { + pw.println("WINDOW MANAGER LAST ANR (dumpsys window lastanr)"); + if (mLastANRState == null) { + pw.println(" <no ANR has occurred since boot>"); + } else { + pw.println(mLastANRState); + } + } + + /** + * Saves information about the state of the window manager at + * the time an ANR occurred before anything else in the system changes + * in response. + * + * @param appWindowToken The application that ANR'd, may be null. + * @param windowState The window that ANR'd, may be null. + * @param reason The reason for the ANR, may be null. + */ + public void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState, + String reason) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 1024); + pw.println(" ANR time: " + DateFormat.getInstance().format(new Date())); + if (appWindowToken != null) { + pw.println(" Application at fault: " + appWindowToken.stringName); + } + if (windowState != null) { + pw.println(" Window at fault: " + windowState.mAttrs.getTitle()); + } + if (reason != null) { + pw.println(" Reason: " + reason); + } + pw.println(); + dumpWindowsNoHeaderLocked(pw, true, null); + pw.close(); + mLastANRState = sw.toString(); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission("android.permission.DUMP") + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump WindowManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + boolean dumpAll = false; + + int opti = 0; + while (opti < args.length) { + String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + if ("-a".equals(opt)) { + dumpAll = true; + } else if ("-h".equals(opt)) { + pw.println("Window manager dump options:"); + pw.println(" [-a] [-h] [cmd] ..."); + pw.println(" cmd may be one of:"); + pw.println(" l[astanr]: last ANR information"); + pw.println(" p[policy]: policy state"); + pw.println(" a[animator]: animator state"); + pw.println(" s[essions]: active sessions"); + pw.println(" d[isplays]: active display contents"); + pw.println(" t[okens]: token list"); + pw.println(" w[indows]: window list"); + pw.println(" cmd may also be a NAME to dump windows. NAME may"); + pw.println(" be a partial substring in a window name, a"); + pw.println(" Window hex object identifier, or"); + pw.println(" \"all\" for all windows, or"); + pw.println(" \"visible\" for the visible windows."); + pw.println(" -a: include all available server state."); + return; + } else { + pw.println("Unknown argument: " + opt + "; use -h for help"); + } + } + + // Is the caller requesting to dump a particular piece of data? + if (opti < args.length) { + String cmd = args[opti]; + opti++; + if ("lastanr".equals(cmd) || "l".equals(cmd)) { + synchronized(mWindowMap) { + dumpLastANRLocked(pw); + } + return; + } else if ("policy".equals(cmd) || "p".equals(cmd)) { + synchronized(mWindowMap) { + dumpPolicyLocked(pw, args, true); + } + return; + } else if ("animator".equals(cmd) || "a".equals(cmd)) { + synchronized(mWindowMap) { + dumpAnimatorLocked(pw, args, true); + } + return; + } else if ("sessions".equals(cmd) || "s".equals(cmd)) { + synchronized(mWindowMap) { + dumpSessionsLocked(pw, true); + } + return; + } else if ("displays".equals(cmd) || "d".equals(cmd)) { + synchronized(mWindowMap) { + dumpDisplayContentsLocked(pw, true); + } + return; + } else if ("tokens".equals(cmd) || "t".equals(cmd)) { + synchronized(mWindowMap) { + dumpTokensLocked(pw, true); + } + return; + } else if ("windows".equals(cmd) || "w".equals(cmd)) { + synchronized(mWindowMap) { + dumpWindowsLocked(pw, true, null); + } + return; + } else if ("all".equals(cmd) || "a".equals(cmd)) { + synchronized(mWindowMap) { + dumpWindowsLocked(pw, true, null); + } + return; + } else { + // Dumping a single name? + if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { + pw.println("Bad window command, or no windows match: " + cmd); + pw.println("Use -h for help."); + } + return; + } + } + + synchronized(mWindowMap) { + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpLastANRLocked(pw); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpPolicyLocked(pw, args, dumpAll); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpAnimatorLocked(pw, args, dumpAll); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpSessionsLocked(pw, dumpAll); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpDisplayContentsLocked(pw, dumpAll); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpTokensLocked(pw, dumpAll); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpWindowsLocked(pw, dumpAll, null); + } + } + + // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection). + @Override + public void monitor() { + synchronized (mWindowMap) { } + } + + public interface OnHardKeyboardStatusChangeListener { + public void onHardKeyboardStatusChange(boolean available, boolean enabled); + } + + void debugLayoutRepeats(final String msg, int pendingLayoutChanges) { + if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) { + Slog.v(TAG, "Layouts looping: " + msg + ", mPendingLayoutChanges = 0x" + + Integer.toHexString(pendingLayoutChanges)); + } + } + + private DisplayContent newDisplayContentLocked(final Display display) { + DisplayContent displayContent = new DisplayContent(display, this); + final int displayId = display.getDisplayId(); + mDisplayContents.put(displayId, displayContent); + + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final Rect rect = new Rect(); + mDisplaySettings.getOverscanLocked(displayInfo.name, rect); + synchronized (displayContent.mDisplaySizeLock) { + displayInfo.overscanLeft = rect.left; + displayInfo.overscanTop = rect.top; + displayInfo.overscanRight = rect.right; + displayInfo.overscanBottom = rect.bottom; + mDisplayManagerService.setDisplayInfoOverrideFromWindowManager( + displayId, displayInfo); + } + configureDisplayPolicyLocked(displayContent); + + // TODO: Create an input channel for each display with touch capability. + if (displayId == Display.DEFAULT_DISPLAY) { + displayContent.mTapDetector = new StackTapPointerEventListener(this, displayContent); + registerPointerEventListener(displayContent.mTapDetector); + } + + return displayContent; + } + + public void createDisplayContentLocked(final Display display) { + if (display == null) { + throw new IllegalArgumentException("getDisplayContent: display must not be null"); + } + getDisplayContentLocked(display.getDisplayId()); + } + + /** + * Retrieve the DisplayContent for the specified displayId. Will create a new DisplayContent if + * there is a Display for the displayId. + * @param displayId The display the caller is interested in. + * @return The DisplayContent associated with displayId or null if there is no Display for it. + */ + public DisplayContent getDisplayContentLocked(final int displayId) { + DisplayContent displayContent = mDisplayContents.get(displayId); + if (displayContent == null) { + final Display display = mDisplayManager.getDisplay(displayId); + if (display != null) { + displayContent = newDisplayContentLocked(display); + } + } + return displayContent; + } + + // There is an inherent assumption that this will never return null. + public DisplayContent getDefaultDisplayContentLocked() { + return getDisplayContentLocked(Display.DEFAULT_DISPLAY); + } + + public WindowList getDefaultWindowListLocked() { + return getDefaultDisplayContentLocked().getWindowList(); + } + + public DisplayInfo getDefaultDisplayInfoLocked() { + return getDefaultDisplayContentLocked().getDisplayInfo(); + } + + /** + * Return the list of WindowStates associated on the passed display. + * @param display The screen to return windows from. + * @return The list of WindowStates on the screen, or null if the there is no screen. + */ + public WindowList getWindowListLocked(final Display display) { + return getWindowListLocked(display.getDisplayId()); + } + + /** + * Return the list of WindowStates associated on the passed display. + * @param displayId The screen to return windows from. + * @return The list of WindowStates on the screen, or null if the there is no screen. + */ + public WindowList getWindowListLocked(final int displayId) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + return displayContent != null ? displayContent.getWindowList() : null; + } + + @Override + public void onDisplayAdded(int displayId) { + mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0)); + } + + private void handleDisplayAddedLocked(int displayId) { + final Display display = mDisplayManager.getDisplay(displayId); + if (display != null) { + createDisplayContentLocked(display); + displayReady(displayId); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_REMOVED, displayId, 0)); + } + + private void handleDisplayRemovedLocked(int displayId) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + mDisplayContents.delete(displayId); + displayContent.close(); + if (displayId == Display.DEFAULT_DISPLAY) { + unregisterPointerEventListener(displayContent.mTapDetector); + } + WindowList windows = displayContent.getWindowList(); + while (!windows.isEmpty()) { + final WindowState win = windows.get(windows.size() - 1); + removeWindowLocked(win.mSession, win); + } + } + mAnimator.removeDisplayLocked(displayId); + } + + @Override + public void onDisplayChanged(int displayId) { + mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_CHANGED, displayId, 0)); + } + + private void handleDisplayChangedLocked(int displayId) { + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent != null) { + displayContent.updateDisplayInfo(); + } + } + + @Override + public Object getWindowManagerLock() { + return mWindowMap; + } +} |