summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/WindowManagerService.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /services/java/com/android/server/WindowManagerService.java
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'services/java/com/android/server/WindowManagerService.java')
-rw-r--r--services/java/com/android/server/WindowManagerService.java7408
1 files changed, 7408 insertions, 0 deletions
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
new file mode 100644
index 0000000..4ad2c42
--- /dev/null
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -0,0 +1,7408 @@
+/*
+ * 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;
+
+import static android.os.LocalPowerManager.CHEEK_EVENT;
+import static android.os.LocalPowerManager.OTHER_EVENT;
+import static android.os.LocalPowerManager.TOUCH_EVENT;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_HARDWARE;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_PUSH_BUFFERS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.policy.PolicyManager;
+import com.android.server.KeyInputQueue.QueuedEvent;
+import com.android.server.am.BatteryStats;
+
+import android.Manifest;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.LocalPowerManager;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.TokenWatcher;
+import android.provider.Settings;
+import android.util.Config;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.IOnKeyguardExitResult;
+import android.view.IRotationWatcher;
+import android.view.IWindow;
+import android.view.IWindowManager;
+import android.view.IWindowSession;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RawInputEvent;
+import android.view.Surface;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/** {@hide} */
+public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor {
+ static final String TAG = "WindowManager";
+ static final boolean DEBUG = false;
+ static final boolean DEBUG_FOCUS = false;
+ static final boolean DEBUG_ANIM = false;
+ static final boolean DEBUG_INPUT = false;
+ static final boolean DEBUG_VISIBILITY = false;
+ static final boolean DEBUG_ORIENTATION = false;
+ static final boolean DEBUG_APP_TRANSITIONS = false;
+ static final boolean DEBUG_STARTING_WINDOW = false;
+ static final boolean DEBUG_REORDER = false;
+ static final boolean SHOW_TRANSACTIONS = false;
+
+ static final boolean PROFILE_ORIENTATION = false;
+ static final boolean BLUR = true;
+ static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+
+ static final int LOG_WM_NO_SURFACE_MEMORY = 31000;
+
+ /** How long to wait for first key repeat, in milliseconds */
+ static final int KEY_REPEAT_FIRST_DELAY = 750;
+
+ /** How long to wait for subsequent key repeats, in milliseconds */
+ static final int KEY_REPEAT_DELAY = 50;
+
+ /** 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;
+
+ /** The maximum length we will accept for a loaded animation duration:
+ * this is 10 seconds.
+ */
+ static final int MAX_ANIMATION_DURATION = 10*1000;
+
+ private static final String SYSTEM_SECURE = "ro.secure";
+
+ /**
+ * Condition waited on by {@link #reenableKeyguard} to know the call to
+ * the window policy has finished.
+ */
+ private boolean mWaitingUntilKeyguardReenabled = false;
+
+
+ final TokenWatcher mKeyguardDisabled = new TokenWatcher(
+ new Handler(), "WindowManagerService.mKeyguardDisabled") {
+ public void acquired() {
+ mPolicy.enableKeyguard(false);
+ }
+ public void released() {
+ synchronized (mKeyguardDisabled) {
+ mPolicy.enableKeyguard(true);
+ mWaitingUntilKeyguardReenabled = false;
+ mKeyguardDisabled.notifyAll();
+ }
+ }
+ };
+
+ final Context mContext;
+
+ final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
+
+ final IActivityManager mActivityManager;
+
+ final IBatteryStats mBatteryStats;
+
+ /**
+ * 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.
+ */
+ 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>();
+
+ /**
+ * The same tokens as mTokenMap, stored in a list for efficient iteration
+ * over them.
+ */
+ final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();
+
+ /**
+ * Z-ordered (bottom-most first) list of all application tokens, for
+ * controlling the ordering of windows in different applications. This
+ * contains WindowToken objects.
+ */
+ final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
+
+ /**
+ * Application tokens that are in the process of exiting, but still
+ * on screen for animations.
+ */
+ final ArrayList<AppWindowToken> mExitingAppTokens = new ArrayList<AppWindowToken>();
+
+ /**
+ * 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>();
+
+ /**
+ * Z-ordered (bottom-most first) list of all Window objects.
+ */
+ final ArrayList mWindows = new ArrayList();
+
+ /**
+ * Windows that are being resized. Used so we can tell the client about
+ * the resize after closing the transaction in which we resized the
+ * underlying surface.
+ */
+ final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>();
+
+ /**
+ * Windows whose animations have ended and now must be removed.
+ */
+ final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();
+
+ /**
+ * 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;
+
+ SurfaceSession mFxSession;
+ Surface mDimSurface;
+ boolean mDimShown;
+ Surface mBlurSurface;
+ boolean mBlurShown;
+
+ int mTransactionSequence = 0;
+
+ final float[] mTmpFloats = new float[9];
+
+ boolean mDisplayEnabled = false;
+ boolean mSystemBooted = false;
+ int mRotation = 0;
+ int mRequestedRotation = 0;
+ int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ ArrayList<IRotationWatcher> mRotationWatchers
+ = new ArrayList<IRotationWatcher>();
+
+ boolean mLayoutNeeded = true;
+ boolean mAnimationPending = false;
+ boolean mSurfacesChanged = false;
+ boolean mDisplayFrozen = false;
+ boolean mWindowsFreezingScreen = false;
+ long mFreezeGcPending = 0;
+ int mAppsFreezingScreen = 0;
+
+ // State management of app transitions. When we are preparing for a
+ // transition, mNextAppTransition will be the kind of transition to
+ // perform or TRANSIT_NONE if we are not waiting. If we are waiting,
+ // mOpeningApps and mClosingApps are the lists of tokens that will be
+ // made visible or hidden at the next transition.
+ int mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+ boolean mAppTransitionReady = false;
+ boolean mAppTransitionTimeout = false;
+ boolean mStartingIconInTransition = false;
+ boolean mSkipAppTransitionAnimation = false;
+ final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
+
+ //flag to detect fat touch events
+ boolean mFatTouch = false;
+ Display mDisplay;
+
+ H mH = new H();
+
+ WindowState mCurrentFocus = null;
+ WindowState mLastFocus = null;
+
+ AppWindowToken mFocusedApp = null;
+
+ PowerManagerService mPowerManager;
+
+ float mWindowAnimationScale = 1.0f;
+ float mTransitionAnimationScale = 0.0f;
+
+ final KeyWaiter mKeyWaiter = new KeyWaiter();
+ final KeyQ mQueue;
+ final InputDispatcherThread mInputThread;
+
+ // Who is holding the screen on.
+ Session mHoldingScreenOn;
+
+ /**
+ * Whether the UI is currently running in touch mode (not showing
+ * navigational focus because the user is directly pressing the screen).
+ */
+ boolean mInTouchMode = false;
+
+ private ViewServer mViewServer;
+
+ public static WindowManagerService main(
+ Context context, PowerManagerService pm) {
+ WMThread thr = new WMThread(context, pm);
+ thr.start();
+
+ synchronized (thr) {
+ while (thr.mService == null) {
+ try {
+ thr.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ return thr.mService;
+ }
+
+ static class WMThread extends Thread {
+ WindowManagerService mService;
+
+ private final Context mContext;
+ private final PowerManagerService mPM;
+
+ public WMThread(Context context, PowerManagerService pm) {
+ super("WindowManager");
+ mContext = context;
+ mPM = pm;
+ }
+
+ public void run() {
+ Looper.prepare();
+ WindowManagerService s = new WindowManagerService(mContext, mPM);
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_DISPLAY);
+
+ synchronized (this) {
+ mService = s;
+ notifyAll();
+ }
+
+ Looper.loop();
+ }
+ }
+
+ static class PolicyThread extends Thread {
+ private final WindowManagerPolicy mPolicy;
+ private final WindowManagerService mService;
+ private final Context mContext;
+ private final PowerManagerService mPM;
+ boolean mRunning = false;
+
+ public PolicyThread(WindowManagerPolicy policy,
+ WindowManagerService service, Context context,
+ PowerManagerService pm) {
+ super("WindowManagerPolicy");
+ mPolicy = policy;
+ mService = service;
+ mContext = context;
+ mPM = pm;
+ }
+
+ public void run() {
+ Looper.prepare();
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // Log.VERBOSE, "WindowManagerPolicy"));
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ mPolicy.init(mContext, mService, mPM);
+
+ synchronized (this) {
+ mRunning = true;
+ notifyAll();
+ }
+
+ Looper.loop();
+ }
+ }
+
+ private WindowManagerService(Context context, PowerManagerService pm) {
+ mContext = context;
+
+ mPowerManager = pm;
+ mPowerManager.setPolicy(mPolicy);
+
+ mActivityManager = ActivityManagerNative.getDefault();
+ mBatteryStats = BatteryStats.getService();
+
+ // Get persisted window scale setting
+ mWindowAnimationScale = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+ mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+
+ mQueue = new KeyQ();
+
+ mInputThread = new InputDispatcherThread();
+
+ PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
+ thr.start();
+
+ synchronized (thr) {
+ while (!thr.mRunning) {
+ try {
+ thr.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ mInputThread.start();
+
+ // Add ourself to the Watchdog monitors.
+ Watchdog.getInstance().addMonitor(this);
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ // The activity manager only throws security exceptions, so let's
+ // log all others.
+ if (!(e instanceof SecurityException)) {
+ Log.e(TAG, "Window Manager Crash", e);
+ }
+ throw e;
+ }
+ }
+
+ private void placeWindowAfter(Object pos, WindowState window) {
+ final int i = mWindows.indexOf(pos);
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Adding window " + window + " at "
+ + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
+ mWindows.add(i+1, window);
+ }
+
+ private void placeWindowBefore(Object pos, WindowState window) {
+ final int i = mWindows.indexOf(pos);
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Adding window " + window + " at "
+ + i + " of " + mWindows.size() + " (before " + pos + ")");
+ mWindows.add(i, window);
+ }
+
+ //This method finds out the index of a window that has the same app token as
+ //win. used for z ordering the windows in mWindows
+ private int findIdxBasedOnAppTokens(WindowState win) {
+ //use a local variable to cache mWindows
+ ArrayList localmWindows = mWindows;
+ int jmax = localmWindows.size();
+ if(jmax == 0) {
+ return -1;
+ }
+ for(int j = (jmax-1); j >= 0; j--) {
+ WindowState wentry = (WindowState)localmWindows.get(j);
+ if(wentry.mAppToken == win.mAppToken) {
+ return j;
+ }
+ }
+ return -1;
+ }
+
+ private void addWindowToListInOrderLocked(WindowState win) {
+ final IWindow client = win.mClient;
+ final WindowToken token = win.mToken;
+ final ArrayList localmWindows = mWindows;
+
+ final int N = localmWindows.size();
+ final WindowState attached = win.mAttachedWindow;
+ int i;
+ if (attached == null) {
+ int tokenWindowsPos = token.windows.size();
+ if (token.appWindowToken != null) {
+ int index = tokenWindowsPos-1;
+ if (index >= 0) {
+ // If this application has existing windows, we
+ // simply place the new window on top of them... but
+ // keep the starting window on top.
+ if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
+ // Base windows go behind everything else.
+ placeWindowBefore(token.windows.get(0), win);
+ tokenWindowsPos = 0;
+ } else {
+ AppWindowToken atoken = win.mAppToken;
+ if (atoken != null &&
+ token.windows.get(index) == atoken.startingWindow) {
+ placeWindowBefore(token.windows.get(index), win);
+ tokenWindowsPos--;
+ } else {
+ int newIdx = findIdxBasedOnAppTokens(win);
+ if(newIdx != -1) {
+ //there is a window above this one associated with the same
+ //apptoken note that the window could be a floating window
+ //that was created later or a window at the top of the list of
+ //windows associated with this token.
+ localmWindows.add(newIdx+1, win);
+ }
+ }
+ }
+ } else {
+ if (localLOGV) Log.v(
+ TAG, "Figuring out where to add app window "
+ + client.asBinder() + " (token=" + token + ")");
+ // Figure out where the window should go, based on the
+ // order of applications.
+ final int NA = mAppTokens.size();
+ Object pos = null;
+ for (i=NA-1; i>=0; i--) {
+ AppWindowToken t = mAppTokens.get(i);
+ if (t == token) {
+ i--;
+ break;
+ }
+ if (t.windows.size() > 0) {
+ pos = t.windows.get(0);
+ }
+ }
+ // We now know the index into the apps. If we found
+ // an app window above, that gives us the position; else
+ // we need to look some more.
+ if (pos != null) {
+ // Move behind any windows attached to this one.
+ WindowToken atoken =
+ mTokenMap.get(((WindowState)pos).mClient.asBinder());
+ if (atoken != null) {
+ final int NC = atoken.windows.size();
+ if (NC > 0) {
+ WindowState bottom = atoken.windows.get(0);
+ if (bottom.mSubLayer < 0) {
+ pos = bottom;
+ }
+ }
+ }
+ placeWindowBefore(pos, win);
+ } else {
+ while (i >= 0) {
+ AppWindowToken t = mAppTokens.get(i);
+ final int NW = t.windows.size();
+ if (NW > 0) {
+ pos = t.windows.get(NW-1);
+ break;
+ }
+ i--;
+ }
+ if (pos != null) {
+ // Move in front of any windows attached to this
+ // one.
+ WindowToken atoken =
+ mTokenMap.get(((WindowState)pos).mClient.asBinder());
+ if (atoken != null) {
+ final int NC = atoken.windows.size();
+ if (NC > 0) {
+ WindowState top = atoken.windows.get(NC-1);
+ if (top.mSubLayer >= 0) {
+ pos = top;
+ }
+ }
+ }
+ placeWindowAfter(pos, win);
+ } else {
+ // Just search for the start of this layer.
+ final int myLayer = win.mBaseLayer;
+ for (i=0; i<N; i++) {
+ WindowState w = (WindowState)localmWindows.get(i);
+ if (w.mBaseLayer > myLayer) {
+ break;
+ }
+ }
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Adding window " + win + " at "
+ + i + " of " + N);
+ localmWindows.add(i, win);
+ }
+ }
+ }
+ } else {
+ // Figure out where window should go, based on layer.
+ final int myLayer = win.mBaseLayer;
+ for (i=N-1; i>=0; i--) {
+ if (((WindowState)localmWindows.get(i)).mBaseLayer <= myLayer) {
+ i++;
+ break;
+ }
+ }
+ if (i < 0) i = 0;
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Adding window " + win + " at "
+ + i + " of " + N);
+ localmWindows.add(i, win);
+ }
+ token.windows.add(tokenWindowsPos, win);
+
+ } else {
+ // Figure out this window's ordering relative to the window
+ // it is attached to.
+ final int NA = token.windows.size();
+ final int sublayer = win.mSubLayer;
+ int largestSublayer = Integer.MIN_VALUE;
+ WindowState windowWithLargestSublayer = null;
+ for (i=0; i<NA; i++) {
+ WindowState w = token.windows.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) {
+ 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) {
+ token.windows.add(i, win);
+ placeWindowBefore(w, win);
+ break;
+ }
+ }
+ }
+ if (i >= NA) {
+ token.windows.add(win);
+ if (sublayer < 0) {
+ placeWindowBefore(attached, win);
+ } else {
+ placeWindowAfter(largestSublayer >= 0
+ ? windowWithLargestSublayer
+ : attached,
+ win);
+ }
+ }
+ }
+
+ if (win.mAppToken != null) {
+ win.mAppToken.allAppWindows.add(win);
+ }
+ }
+
+ public int addWindow(Session session, IWindow client,
+ WindowManager.LayoutParams attrs, int viewVisibility,
+ Rect outCoveredInsets) {
+ int res = mPolicy.checkAddPermission(attrs);
+ if (res != WindowManagerImpl.ADD_OKAY) {
+ return res;
+ }
+
+ boolean reportNewConfig = false;
+ WindowState attachedWindow = null;
+ WindowState win = null;
+
+ synchronized(mWindowMap) {
+ // Instantiating a Display requires talking with the simulator,
+ // so don't do it until we know the system is mostly up and
+ // running.
+ if (mDisplay == null) {
+ WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = wm.getDefaultDisplay();
+ mQueue.setDisplay(mDisplay);
+ reportNewConfig = true;
+ }
+
+ if (mWindowMap.containsKey(client.asBinder())) {
+ Log.w(TAG, "Window " + client + " is already added");
+ return WindowManagerImpl.ADD_DUPLICATE_ADD;
+ }
+
+ if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
+ attachedWindow = windowForClientLocked(session, attrs.token);
+ if (attachedWindow == null) {
+ Log.w(TAG, "Attempted to add window with token that is not a window: "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
+ }
+ if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
+ && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
+ Log.w(TAG, "Attempted to add window with token that is a sub-window: "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
+ }
+ }
+
+ boolean addToken = false;
+ WindowToken token = mTokenMap.get(attrs.token);
+ if (token == null) {
+ if (attrs.type >= FIRST_APPLICATION_WINDOW
+ && attrs.type <= LAST_APPLICATION_WINDOW) {
+ Log.w(TAG, "Attempted to add application window with unknown token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ }
+ token = new WindowToken(attrs.token);
+ addToken = true;
+ } else if (attrs.type >= FIRST_APPLICATION_WINDOW
+ && attrs.type <= LAST_APPLICATION_WINDOW) {
+ AppWindowToken atoken = token.appWindowToken;
+ if (atoken == null) {
+ Log.w(TAG, "Attempted to add window with non-application token "
+ + token + ". Aborting.");
+ return WindowManagerImpl.ADD_NOT_APP_TOKEN;
+ } else if (atoken.removed) {
+ Log.w(TAG, "Attempted to add window with exiting application token "
+ + token + ". Aborting.");
+ return WindowManagerImpl.ADD_APP_EXITING;
+ }
+ if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
+ // No need for this guy!
+ if (localLOGV) Log.v(
+ TAG, "**** NO NEED TO START: " + attrs.getTitle());
+ return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;
+ }
+ }
+
+ win = new WindowState(session, client, token,
+ attachedWindow, attrs, viewVisibility);
+ if (win.mDeathRecipient == null) {
+ // Client has apparently died, so there is no reason to
+ // continue.
+ Log.w(TAG, "Adding window client " + client.asBinder()
+ + " that is dead, aborting.");
+ return WindowManagerImpl.ADD_APP_EXITING;
+ }
+
+ mPolicy.adjustWindowParamsLw(win.mAttrs);
+
+ res = mPolicy.prepareAddWindowLw(win, attrs);
+ if (res != WindowManagerImpl.ADD_OKAY) {
+ return res;
+ }
+
+ // From now on, no exceptions or errors allowed!
+
+ res = WindowManagerImpl.ADD_OKAY;
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (addToken) {
+ mTokenMap.put(attrs.token, token);
+ mTokenList.add(token);
+ }
+ win.attach();
+ mWindowMap.put(client.asBinder(), win);
+
+ if (attrs.type == TYPE_APPLICATION_STARTING &&
+ token.appWindowToken != null) {
+ token.appWindowToken.startingWindow = win;
+ }
+
+ addWindowToListInOrderLocked(win);
+
+ assignLayersLocked();
+ // Don't do layout here, the window must call
+ // relayout to be displayed, so we'll do it there.
+
+ //dump();
+
+ Binder.restoreCallingIdentity(origId);
+
+ win.mEnterAnimationPending = true;
+
+ mPolicy.getCoveredInsetHintLw(attrs, outCoveredInsets);
+
+ if (mInTouchMode) {
+ res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;
+ }
+ if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) {
+ res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;
+ }
+
+ if (win.canReceiveKeys()) {
+ updateFocusedWindowLocked();
+ }
+
+ if (localLOGV) Log.v(
+ TAG, "New client " + client.asBinder()
+ + ": window=" + win);
+ }
+
+ if (reportNewConfig) {
+ final long origId = Binder.clearCallingIdentity();
+ sendNewConfiguration();
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return res;
+ }
+
+ public void removeWindow(Session session, IWindow client) {
+ synchronized(mWindowMap) {
+ WindowState win = windowForClientLocked(session, client);
+ if (win == null) {
+ return;
+ }
+ removeWindowLocked(session, win);
+ }
+ }
+
+ public void removeWindowLocked(Session session, WindowState win) {
+
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Remove " + win + " client="
+ + Integer.toHexString(System.identityHashCode(
+ win.mClient.asBinder()))
+ + ", surface=" + win.mSurface);
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (DEBUG_APP_TRANSITIONS) Log.v(
+ TAG, "Remove " + win + ": mSurface=" + win.mSurface
+ + " mExiting=" + win.mExiting
+ + " isAnimating=" + win.isAnimating()
+ + " app-animation="
+ + (win.mAppToken != null ? win.mAppToken.animation : null)
+ + " inPendingTransaction="
+ + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ + " mDisplayFrozen=" + mDisplayFrozen);
+
+ // First, see if we need to run an animation. If we do, we have
+ // to hold off on removing the window until the animation is done.
+ // If the display is frozen, just remove immediately, since the
+ // animation wouldn't be seen.
+ if (win.mSurface != null && !mDisplayFrozen) {
+ // If we are not currently running the exit animation, we
+ // need to see about starting one.
+ if (!win.mExiting && !win.mDrawPending && !win.mCommitDrawPending) {
+ int transit = WindowManagerPolicy.TRANSIT_EXIT;
+ if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
+ transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+ }
+ // Try starting an animation.
+ if (applyAnimationLocked(win, transit, false)) {
+ win.mExiting = true;
+ }
+ }
+ if (win.mExiting || win.isAnimating()) {
+ // The exit animation is running... wait for it!
+ //Log.i(TAG, "*** Running exit animation...");
+ win.mExiting = true;
+ win.mRemoveOnExit = true;
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ updateFocusedWindowLocked();
+ if (win.mAppToken != null) {
+ win.mAppToken.updateReportedVisibilityLocked();
+ }
+ //dump();
+ Binder.restoreCallingIdentity(origId);
+ return;
+ }
+ }
+
+ removeWindowInnerLocked(session, win);
+ updateFocusedWindowLocked();
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ private void removeWindowInnerLocked(Session session, WindowState win) {
+ mKeyWaiter.releasePendingPointerLocked(win.mSession);
+ mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+
+ mPolicy.removeWindowLw(win);
+ win.removeLocked();
+
+ mWindowMap.remove(win.mClient.asBinder());
+ mWindows.remove(win);
+
+ final WindowToken token = win.mToken;
+ final AppWindowToken atoken = win.mAppToken;
+ token.windows.remove(win);
+ if (atoken != null) {
+ atoken.allAppWindows.remove(win);
+ }
+ if (localLOGV) Log.v(
+ TAG, "**** Removing window " + win + ": count="
+ + token.windows.size());
+ if (token.windows.size() == 0) {
+ if (atoken != token) {
+ mTokenMap.remove(token.token);
+ mTokenList.remove(token);
+ } else {
+ atoken.firstWindowDrawn = false;
+ }
+ }
+
+ if (atoken != null) {
+ if (atoken.startingWindow == win) {
+ atoken.startingWindow = null;
+ } else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
+ // If this is the last window and we had requested a starting
+ // transition window, well there is no point now.
+ atoken.startingData = null;
+ } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
+ // If this is the last window except for a starting transition
+ // window, we need to get rid of the starting transition.
+ if (DEBUG_STARTING_WINDOW) {
+ Log.v(TAG, "Schedule remove starting " + token
+ + ": no more real windows");
+ }
+ Message m = mH.obtainMessage(H.REMOVE_STARTING, atoken);
+ mH.sendMessage(m);
+ }
+ }
+
+ if (!mInLayout) {
+ assignLayersLocked();
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ if (win.mAppToken != null) {
+ win.mAppToken.updateReportedVisibilityLocked();
+ }
+ }
+ }
+
+ private void setTransparentRegionWindow(Session session, IWindow client, Region region) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mWindowMap) {
+ WindowState w = windowForClientLocked(session, client);
+ if ((w != null) && (w.mSurface != null)) {
+ Surface.openTransaction();
+ try {
+ w.mSurface.setTransparentRegionHint(region);
+ } finally {
+ Surface.closeTransaction();
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+
+ public int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs,
+ int requestedWidth, int requestedHeight, int viewVisibility, Rect outFrame,
+ Rect outCoveredInsets, Surface outSurface) {
+ boolean displayed = false;
+ boolean inTouchMode;
+
+ long origId = Binder.clearCallingIdentity();
+
+ synchronized(mWindowMap) {
+ WindowState win = windowForClientLocked(session, client);
+ if (win == null) {
+ return 0;
+ }
+ win.mRequestedWidth = requestedWidth;
+ win.mRequestedHeight = requestedHeight;
+
+ if (attrs != null) {
+ mPolicy.adjustWindowParamsLw(attrs);
+ }
+
+ boolean windowfocusabilityChanged = attrs != null &&
+ ((attrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) !=
+ (win.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE));
+
+ int attrChanges = 0;
+ if (attrs != null) attrChanges = win.mAttrs.copyFrom(attrs);
+
+ if (localLOGV) Log.v(
+ TAG, "Relayout given client " + client.asBinder()
+ + " (" + win.mAttrs.getTitle() + ")");
+
+
+ if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
+ win.mAlpha = attrs.alpha;
+ }
+
+ final boolean scaledWindow =
+ ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);
+
+ if (scaledWindow) {
+ // requested{Width|Height} Surface's physical size
+ // attrs.{width|height} Size on screen
+ win.mHScale = (attrs.width != requestedWidth) ?
+ (attrs.width / (float)requestedWidth) : 1.0f;
+ win.mVScale = (attrs.height != requestedHeight) ?
+ (attrs.height / (float)requestedHeight) : 1.0f;
+ }
+
+ boolean focusMayChange = win.mViewVisibility != viewVisibility
+ || windowfocusabilityChanged
+ || (!win.mRelayoutCalled);
+ win.mRelayoutCalled = true;
+ win.mViewVisibility = viewVisibility;
+ if (viewVisibility == View.VISIBLE &&
+ (win.mAppToken == null || !win.mAppToken.clientHidden)) {
+ displayed = !win.isVisible();
+ if (win.mExiting) {
+ win.mExiting = false;
+ win.mAnimation = null;
+ }
+ if (win.mDestroying) {
+ win.mDestroying = false;
+ mDestroySurface.remove(win);
+ }
+ if (displayed && win.mSurface != null && !win.mDrawPending
+ && !win.mCommitDrawPending && !mDisplayFrozen) {
+ applyEnterAnimationLocked(win);
+ }
+ try {
+ Surface surface = win.createSurfaceLocked();
+ if (surface != null) {
+ outSurface.copyFrom(surface);
+ } else {
+ outSurface.clear();
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Exception thrown when creating surface for client "
+ + client + " (" + win.mAttrs.getTitle() + ")",
+ e);
+ Binder.restoreCallingIdentity(origId);
+ return 0;
+ }
+ if (displayed) {
+ focusMayChange = true;
+ }
+ } else {
+ if (win.mSurface != null) {
+ // If we are not currently running the exit animation, we
+ // need to see about starting one.
+ if (!win.mExiting) {
+ // Try starting an animation; if there isn't one, we
+ // can destroy the surface right away.
+ int transit = WindowManagerPolicy.TRANSIT_EXIT;
+ if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
+ transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+ }
+ if (!win.mExiting && !win.mDrawPending &&
+ applyAnimationLocked(win, transit, false)) {
+ win.mExiting = true;
+ mKeyWaiter.finishedKey(session, client, true,
+ KeyWaiter.RETURN_NOTHING);
+ } else if (win.isAnimating()) {
+ // Currently in a hide animation... turn this into
+ // an exit.
+ win.mExiting = true;
+ } else {
+ win.destroySurfaceLocked();
+ }
+ }
+ }
+ outSurface.clear();
+ }
+
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ if (win.mAppToken != null) {
+ win.mAppToken.updateReportedVisibilityLocked();
+ }
+ outFrame.set(win.mFrame);
+ outCoveredInsets.set(win.mCoveredInsets);
+ if (localLOGV) Log.v(
+ TAG, "Relayout given client " + client.asBinder()
+ + ", requestedWidth=" + requestedWidth
+ + ", requestedHeight=" + requestedHeight
+ + ", viewVisibility=" + viewVisibility
+ + "\nRelayout returning frame=" + outFrame
+ + ", surface=" + outSurface);
+
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
+
+ if (focusMayChange) {
+ //System.out.println("Focus may change: " + win.mAttrs.getTitle());
+ updateFocusedWindowLocked();
+ //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
+ }
+
+ inTouchMode = mInTouchMode;
+ }
+
+ Binder.restoreCallingIdentity(origId);
+
+ return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
+ | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
+ }
+
+ public void finishDrawingWindow(Session session, IWindow client) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized(mWindowMap) {
+ WindowState win = windowForClientLocked(session, client);
+ if (win != null && win.finishDrawingLocked()) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
+ if (DEBUG_ANIM) Log.v(TAG, "Loading animations: params package="
+ + (lp != null ? lp.packageName : null)
+ + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
+ if (lp != null && lp.windowAnimations != 0) {
+ // If this is a system resource, don't try to load it from the
+ // application resources. It is nice to avoid loading application
+ // resources if we can.
+ String packageName = lp.packageName != null ? lp.packageName : "android";
+ int resId = lp.windowAnimations;
+ if ((resId&0xFF000000) == 0x01000000) {
+ packageName = "android";
+ }
+ if (DEBUG_ANIM) Log.v(TAG, "Loading animations: picked package="
+ + packageName);
+ return AttributeCache.instance().get(packageName, resId,
+ com.android.internal.R.styleable.WindowAnimation);
+ }
+ return null;
+ }
+
+ private void applyEnterAnimationLocked(WindowState win) {
+ int transit = WindowManagerPolicy.TRANSIT_SHOW;
+ if (win.mEnterAnimationPending) {
+ win.mEnterAnimationPending = false;
+ transit = WindowManagerPolicy.TRANSIT_ENTER;
+ }
+
+ applyAnimationLocked(win, transit, true);
+ }
+
+ private boolean applyAnimationLocked(WindowState win,
+ int transit, boolean isEntrance) {
+ if (win.mAnimating && win.mAnimationIsEntrance == isEntrance) {
+ // If we are trying to apply an animation, but already running
+ // an animation of the same type, then just leave that one alone.
+ return true;
+ }
+
+ // Only apply an animation if the display isn't frozen. If it is
+ // frozen, there is no reason to animate and it can cause strange
+ // artifacts when we unfreeze the display if some different animation
+ // is running.
+ if (!mDisplayFrozen) {
+ int anim = mPolicy.selectAnimationLw(win, transit);
+ int attr = -1;
+ Animation a = null;
+ if (anim != 0) {
+ a = AnimationUtils.loadAnimation(mContext, anim);
+ } else {
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_EXIT:
+ attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_SHOW:
+ attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_HIDE:
+ attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
+ break;
+ }
+ if (attr >= 0) {
+ a = loadAnimation(win.mAttrs, attr);
+ }
+ }
+ if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: win=" + win
+ + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
+ + " mAnimation=" + win.mAnimation
+ + " isEntrance=" + isEntrance);
+ if (a != null) {
+ if (DEBUG_ANIM) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.v(TAG, "Loaded animation " + a + " for " + win, e);
+ }
+ win.setAnimation(a);
+ }
+ } else {
+ win.clearAnimation();
+ }
+
+ return win.mAnimation != null;
+ }
+
+ private Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
+ int anim = 0;
+ Context context = mContext;
+ if (animAttr >= 0) {
+ AttributeCache.Entry ent = getCachedAnimations(lp);
+ if (ent != null) {
+ context = ent.context;
+ anim = ent.array.getResourceId(animAttr, 0);
+ }
+ }
+ if (anim != 0) {
+ return AnimationUtils.loadAnimation(context, anim);
+ }
+ return null;
+ }
+
+ private boolean applyAnimationLocked(AppWindowToken wtoken,
+ WindowManager.LayoutParams lp, int transit, boolean enter) {
+ // Only apply an animation if the display isn't frozen. If it is
+ // frozen, there is no reason to animate and it can cause strange
+ // artifacts when we unfreeze the display if some different animation
+ // is running.
+ if (!mDisplayFrozen) {
+ int animAttr = 0;
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
+ break;
+ }
+ Animation a = loadAnimation(lp, animAttr);
+ if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
+ + " anim=" + a
+ + " animAttr=0x" + Integer.toHexString(animAttr)
+ + " transit=" + transit);
+ if (a != null) {
+ if (DEBUG_ANIM) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
+ }
+ wtoken.setAnimation(a);
+ }
+ } else {
+ wtoken.clearAnimation();
+ }
+
+ return wtoken.animation != null;
+ }
+
+ // -------------------------------------------------------------
+ // Application Window Tokens
+ // -------------------------------------------------------------
+
+ public void validateAppTokens(List tokens) {
+ int v = tokens.size()-1;
+ int m = mAppTokens.size()-1;
+ while (v >= 0 && m >= 0) {
+ AppWindowToken wtoken = mAppTokens.get(m);
+ if (wtoken.removed) {
+ m--;
+ continue;
+ }
+ if (tokens.get(v) != wtoken.token) {
+ Log.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
+ + " @ " + v + ", internal is " + wtoken.token + " @ " + m);
+ }
+ v--;
+ m--;
+ }
+ while (v >= 0) {
+ Log.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v);
+ v--;
+ }
+ while (m >= 0) {
+ AppWindowToken wtoken = mAppTokens.get(m);
+ if (!wtoken.removed) {
+ Log.w(TAG, "Invalid internal token: " + wtoken.token + " @ " + m);
+ }
+ m--;
+ }
+ }
+
+ boolean checkCallingPermission(String permission, String func) {
+ // Quick check: if the calling permission is me, it's all okay.
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return true;
+ }
+
+ if (mContext.checkCallingPermission(permission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + permission;
+ Log.w(TAG, msg);
+ return false;
+ }
+
+ AppWindowToken findAppWindowToken(IBinder token) {
+ WindowToken wtoken = mTokenMap.get(token);
+ if (wtoken == null) {
+ return null;
+ }
+ return wtoken.appWindowToken;
+ }
+
+ public void addAppToken(int addPos, IApplicationToken token,
+ int groupId, int requestedOrientation, boolean fullscreen) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "addAppToken()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ AppWindowToken wtoken = findAppWindowToken(token.asBinder());
+ if (wtoken != null) {
+ Log.w(TAG, "Attempted to add existing app token: " + token);
+ return;
+ }
+ wtoken = new AppWindowToken(token);
+ wtoken.groupId = groupId;
+ wtoken.appFullscreen = fullscreen;
+ wtoken.requestedOrientation = requestedOrientation;
+ mAppTokens.add(addPos, wtoken);
+ if (Config.LOGV) Log.v(TAG, "Adding new app token: " + wtoken);
+ mTokenMap.put(token.asBinder(), wtoken);
+ mTokenList.add(wtoken);
+
+ // Application tokens start out hidden.
+ wtoken.hidden = true;
+ wtoken.hiddenRequested = true;
+
+ //dump();
+ }
+ }
+
+ public void setAppGroupId(IBinder token, int groupId) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppStartingIcon()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ AppWindowToken wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Log.w(TAG, "Attempted to set group id of non-existing app token: " + token);
+ return;
+ }
+ wtoken.groupId = groupId;
+ }
+ }
+
+ public Configuration updateOrientationFromAppTokens(
+ IBinder freezeThisOneIfNeeded) {
+ boolean changed = false;
+ synchronized(mWindowMap) {
+ int pos = mAppTokens.size() - 1;
+ int req = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int curGroup = 0;
+ int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ boolean haveGroup = false;
+ while (pos >= 0) {
+ AppWindowToken wtoken = mAppTokens.get(pos);
+ pos--;
+ if (!haveGroup) {
+ // We ignore any hidden applications on the top.
+ if (wtoken.hiddenRequested || wtoken.willBeHidden) {
+ continue;
+ }
+ haveGroup = true;
+ curGroup = wtoken.groupId;
+ lastOrientation = wtoken.requestedOrientation;
+ } else if (curGroup != wtoken.groupId) {
+ // If we have hit a new application group, and the bottom
+ // of the previous group didn't explicitly say to use
+ // the orientation behind it, then we'll stick with the
+ // user's orientation.
+ if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ break;
+ }
+ }
+ int or = wtoken.requestedOrientation;
+ // If this application has requested an explicit orientation,
+ // then use it.
+ if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
+ or == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
+ or == ActivityInfo.SCREEN_ORIENTATION_SENSOR ||
+ or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR ||
+ or == ActivityInfo.SCREEN_ORIENTATION_USER) {
+ req = or;
+ break;
+ }
+ }
+ if (req != mForcedAppOrientation) {
+ changed = true;
+ mForcedAppOrientation = req;
+ //send a message to Policy indicating orientation change to take
+ //action like disabling/enabling sensors etc.,
+ mPolicy.setCurrentOrientation(req);
+ }
+
+ if (changed) {
+ changed = setRotationUncheckedLocked(
+ WindowManagerPolicy.USE_LAST_ROTATION);
+ if (changed) {
+ if (freezeThisOneIfNeeded != null) {
+ AppWindowToken wtoken = findAppWindowToken(
+ freezeThisOneIfNeeded);
+ if (wtoken != null) {
+ startAppFreezingScreenLocked(wtoken,
+ ActivityInfo.CONFIG_ORIENTATION);
+ }
+ }
+ Configuration config = computeNewConfigurationLocked();
+ if (config != null) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ return config;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppOrientation()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ AppWindowToken wtoken = findAppWindowToken(token.asBinder());
+ if (wtoken == null) {
+ Log.w(TAG, "Attempted to set orientation of non-existing app token: " + token);
+ return;
+ }
+
+ wtoken.requestedOrientation = requestedOrientation;
+ }
+ }
+
+ public int getAppOrientation(IApplicationToken token) {
+ synchronized(mWindowMap) {
+ AppWindowToken wtoken = findAppWindowToken(token.asBinder());
+ if (wtoken == null) {
+ return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ return wtoken.requestedOrientation;
+ }
+ }
+
+ public void setFocusedApp(IBinder token, boolean moveFocusNow) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setFocusedApp()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ boolean changed = false;
+ if (token == null) {
+ if (DEBUG_FOCUS) Log.v(TAG, "Clearing focused app, was " + mFocusedApp);
+ changed = mFocusedApp != null;
+ mFocusedApp = null;
+ mKeyWaiter.tickle();
+ } else {
+ AppWindowToken newFocus = findAppWindowToken(token);
+ if (newFocus == null) {
+ Log.w(TAG, "Attempted to set focus to non-existing app token: " + token);
+ return;
+ }
+ changed = mFocusedApp != newFocus;
+ mFocusedApp = newFocus;
+ if (DEBUG_FOCUS) Log.v(TAG, "Set focused app to: " + mFocusedApp);
+ mKeyWaiter.tickle();
+ }
+
+ if (moveFocusNow && changed) {
+ final long origId = Binder.clearCallingIdentity();
+ updateFocusedWindowLocked();
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ public void prepareAppTransition(int transit) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "prepareAppTransition()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(
+ TAG, "Prepare app transition: transit=" + transit
+ + " mNextAppTransition=" + mNextAppTransition);
+ if (!mDisplayFrozen) {
+ if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
+ mNextAppTransition = transit;
+ }
+ mAppTransitionReady = false;
+ mAppTransitionTimeout = false;
+ mStartingIconInTransition = false;
+ mSkipAppTransitionAnimation = false;
+ mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(H.APP_TRANSITION_TIMEOUT),
+ 5000);
+ }
+ }
+ }
+
+ public void executeAppTransition() {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "executeAppTransition()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(
+ TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition);
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ mAppTransitionReady = true;
+ final long origId = Binder.clearCallingIdentity();
+ performLayoutAndPlaceSurfacesLocked();
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ public void setAppStartingWindow(IBinder token, String pkg,
+ int theme, CharSequence nonLocalizedLabel, int labelRes, int icon,
+ IBinder transferFrom, boolean createIfNeeded) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppStartingIcon()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ if (DEBUG_STARTING_WINDOW) Log.v(
+ TAG, "setAppStartingIcon: token=" + token + " pkg=" + pkg
+ + " transferFrom=" + transferFrom);
+
+ AppWindowToken wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Log.w(TAG, "Attempted to set icon of non-existing app token: " + token);
+ return;
+ }
+
+ // If the display is frozen, we won't do anything until the
+ // actual window is displayed so there is no reason to put in
+ // the starting window.
+ if (mDisplayFrozen) {
+ 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) Log.v(TAG,
+ "Moving existing starting from " + ttoken
+ + " to " + wtoken);
+ final long origId = Binder.clearCallingIdentity();
+
+ // Transfer the starting window over to the new
+ // token.
+ wtoken.startingData = ttoken.startingData;
+ wtoken.startingView = ttoken.startingView;
+ wtoken.startingWindow = startingWindow;
+ ttoken.startingData = null;
+ ttoken.startingView = null;
+ ttoken.startingWindow = null;
+ ttoken.startingMoved = true;
+ startingWindow.mToken = wtoken;
+ startingWindow.mAppToken = wtoken;
+ mWindows.remove(startingWindow);
+ ttoken.windows.remove(startingWindow);
+ ttoken.allAppWindows.remove(startingWindow);
+ addWindowToListInOrderLocked(startingWindow);
+ wtoken.allAppWindows.add(startingWindow);
+
+ // Propagate other interesting state between the
+ // tokens. If the old token is displayed, we should
+ // immediately force the new one to be displayed. If
+ // it is animating, we need to move that animation to
+ // the new one.
+ if (ttoken.allDrawn) {
+ wtoken.allDrawn = true;
+ }
+ if (ttoken.firstWindowDrawn) {
+ wtoken.firstWindowDrawn = true;
+ }
+ if (!ttoken.hidden) {
+ wtoken.hidden = false;
+ wtoken.hiddenRequested = false;
+ wtoken.willBeHidden = false;
+ }
+ if (wtoken.clientHidden != ttoken.clientHidden) {
+ wtoken.clientHidden = ttoken.clientHidden;
+ wtoken.sendAppVisibilityToClients();
+ }
+ if (ttoken.animation != null) {
+ wtoken.animation = ttoken.animation;
+ wtoken.animating = ttoken.animating;
+ wtoken.animLayerAdjustment = ttoken.animLayerAdjustment;
+ ttoken.animation = null;
+ ttoken.animLayerAdjustment = 0;
+ wtoken.updateLayers();
+ ttoken.updateLayers();
+ }
+
+ assignLayersLocked();
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ updateFocusedWindowLocked();
+ 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) Log.v(TAG,
+ "Moving pending starting from " + ttoken
+ + " to " + wtoken);
+ wtoken.startingData = ttoken.startingData;
+ ttoken.startingData = null;
+ ttoken.startingMoved = true;
+ Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ mH.sendMessageAtFrontOfQueue(m);
+ return;
+ }
+ }
+ }
+
+ // There is no existing starting window, and the caller doesn't
+ // want us to create one, so that's it!
+ if (!createIfNeeded) {
+ return;
+ }
+
+ mStartingIconInTransition = true;
+ wtoken.startingData = new StartingData(
+ pkg, theme, nonLocalizedLabel,
+ labelRes, icon);
+ Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ mH.sendMessageAtFrontOfQueue(m);
+ }
+ }
+
+ public void setAppWillBeHidden(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppWillBeHidden()")) {
+ return;
+ }
+
+ AppWindowToken wtoken;
+
+ synchronized(mWindowMap) {
+ wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Log.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token);
+ return;
+ }
+ wtoken.willBeHidden = true;
+ }
+ }
+
+ boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
+ boolean visible, int transit, boolean performLayout) {
+ boolean delayed = false;
+
+ if (wtoken.clientHidden == visible) {
+ wtoken.clientHidden = !visible;
+ wtoken.sendAppVisibilityToClients();
+ }
+
+ wtoken.willBeHidden = false;
+ if (wtoken.hidden == visible) {
+ final int N = wtoken.allAppWindows.size();
+ boolean changed = false;
+ if (DEBUG_APP_TRANSITIONS) Log.v(
+ TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
+ + " performLayout=" + performLayout);
+
+ if (transit != WindowManagerPolicy.TRANSIT_NONE) {
+ applyAnimationLocked(wtoken, lp, transit, visible);
+ changed = true;
+ if (wtoken.animation != null) {
+ delayed = true;
+ }
+ }
+
+ for (int i=0; i<N; i++) {
+ WindowState win = wtoken.allAppWindows.get(i);
+ if (win == wtoken.startingWindow) {
+ continue;
+ }
+
+ if (win.isAnimating()) {
+ delayed = true;
+ }
+
+ //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible());
+ //win.dump(" ");
+ if (visible) {
+ if (!win.isVisible()) {
+ changed = true;
+ }
+ } else if (win.isVisible()) {
+ mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ changed = 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.mDrawPending
+ || swin.mCommitDrawPending)) {
+ swin.mPolicyVisibility = false;
+ }
+ }
+
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken
+ + ": hidden=" + wtoken.hidden + " hiddenRequested="
+ + wtoken.hiddenRequested);
+
+ if (changed && performLayout) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ updateFocusedWindowLocked();
+ }
+ }
+
+ if (wtoken.animation != null) {
+ delayed = true;
+ }
+
+ return delayed;
+ }
+
+ public void setAppVisibility(IBinder token, boolean visible) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppVisibility()")) {
+ return;
+ }
+
+ AppWindowToken wtoken;
+
+ synchronized(mWindowMap) {
+ wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Log.w(TAG, "Attempted to set visibility of non-existing app token: " + token);
+ return;
+ }
+
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.v(TAG, "setAppVisibility(" + token + ", " + visible
+ + "): mNextAppTransition=" + mNextAppTransition
+ + " 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 (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ // Already in requested state, don't do anything more.
+ if (wtoken.hiddenRequested != visible) {
+ return;
+ }
+ wtoken.hiddenRequested = !visible;
+
+ if (DEBUG_APP_TRANSITIONS) Log.v(
+ TAG, "Setting dummy animation on: " + wtoken);
+ wtoken.setDummyAnimation();
+ mOpeningApps.remove(wtoken);
+ mClosingApps.remove(wtoken);
+ wtoken.inPendingTransaction = true;
+ if (visible) {
+ mOpeningApps.add(wtoken);
+ wtoken.allDrawn = false;
+ wtoken.startingDisplayed = false;
+ wtoken.startingMoved = false;
+
+ 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);
+ }
+ return;
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true);
+ wtoken.updateReportedVisibilityLocked();
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ void unsetAppFreezingScreenLocked(AppWindowToken wtoken,
+ boolean unfreezeSurfaceNow, boolean force) {
+ if (wtoken.freezingScreen) {
+ if (DEBUG_ORIENTATION) Log.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.mSurface != null && !w.mOrientationChanging) {
+ w.mOrientationChanging = true;
+ }
+ unfrozeWindows = true;
+ }
+ }
+ if (force || unfrozeWindows) {
+ if (DEBUG_ORIENTATION) Log.v(TAG, "No longer freezing: " + wtoken);
+ wtoken.freezingScreen = false;
+ mAppsFreezingScreen--;
+ }
+ if (unfreezeSurfaceNow) {
+ if (unfrozeWindows) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ if (mAppsFreezingScreen == 0 && !mWindowsFreezingScreen) {
+ stopFreezingDisplayLocked();
+ }
+ }
+ }
+ }
+
+ public void startAppFreezingScreenLocked(AppWindowToken wtoken,
+ int configChanges) {
+ if (DEBUG_ORIENTATION) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.i(TAG, "Set freezing of " + wtoken.appToken
+ + ": hidden=" + wtoken.hidden + " freezing="
+ + wtoken.freezingScreen, e);
+ }
+ if (!wtoken.hiddenRequested) {
+ if (!wtoken.freezingScreen) {
+ wtoken.freezingScreen = true;
+ mAppsFreezingScreen++;
+ if (mAppsFreezingScreen == 1) {
+ startFreezingDisplayLocked();
+ mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(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;
+ }
+ }
+ }
+
+ public void startAppFreezingScreen(IBinder token, int configChanges) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppFreezingScreen()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ if (configChanges == 0 && !mDisplayFrozen) {
+ if (DEBUG_ORIENTATION) Log.v(TAG, "Skipping set freeze of " + token);
+ return;
+ }
+
+ AppWindowToken wtoken = findAppWindowToken(token);
+ if (wtoken == null || wtoken.appToken == null) {
+ Log.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken);
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ startAppFreezingScreenLocked(wtoken, configChanges);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ public void stopAppFreezingScreen(IBinder token, boolean force) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "setAppFreezingScreen()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ AppWindowToken wtoken = findAppWindowToken(token);
+ if (wtoken == null || wtoken.appToken == null) {
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ if (DEBUG_ORIENTATION) Log.v(TAG, "Clear freezing of " + token
+ + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen);
+ unsetAppFreezingScreenLocked(wtoken, true, force);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ public void removeAppToken(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "removeAppToken()")) {
+ return;
+ }
+
+ AppWindowToken wtoken = null;
+ AppWindowToken startingToken = null;
+ boolean delayed = false;
+
+ final long origId = Binder.clearCallingIdentity();
+ synchronized(mWindowMap) {
+ WindowToken basewtoken = mTokenMap.remove(token);
+ mTokenList.remove(basewtoken);
+ if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "Removing app token: " + wtoken);
+ delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_NONE, true);
+ wtoken.inPendingTransaction = false;
+ mOpeningApps.remove(wtoken);
+ if (mClosingApps.contains(wtoken)) {
+ delayed = true;
+ } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ mClosingApps.add(wtoken);
+ delayed = true;
+ }
+ if (DEBUG_APP_TRANSITIONS) Log.v(
+ TAG, "Removing app " + wtoken + " delayed=" + delayed
+ + " animation=" + wtoken.animation
+ + " animating=" + wtoken.animating);
+ if (delayed) {
+ mExitingAppTokens.add(wtoken);
+ } else {
+ mAppTokens.remove(wtoken);
+ }
+ wtoken.removed = true;
+ if (wtoken.startingData != null) {
+ startingToken = wtoken;
+ }
+ unsetAppFreezingScreenLocked(wtoken, true, true);
+ if (mFocusedApp == wtoken) {
+ if (DEBUG_FOCUS) Log.v(TAG, "Removing focused app token:" + wtoken);
+ mFocusedApp = null;
+ updateFocusedWindowLocked();
+ mKeyWaiter.tickle();
+ }
+ } else {
+ Log.w(TAG, "Attempted to remove non-existing app token: " + token);
+ }
+
+ if (!delayed && wtoken != null) {
+ wtoken.updateReportedVisibilityLocked();
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+
+ if (startingToken != null) {
+ if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Schedule remove starting "
+ + startingToken + ": app token removed");
+ Message m = mH.obtainMessage(H.REMOVE_STARTING, startingToken);
+ mH.sendMessage(m);
+ }
+ }
+
+ private boolean tmpRemoveAppWindowsLocked(WindowToken token) {
+ final int NW = token.windows.size();
+ for (int i=0; i<NW; i++) {
+ WindowState win = token.windows.get(i);
+ mWindows.remove(win);
+ int j = win.mChildWindows.size();
+ while (j > 0) {
+ j--;
+ mWindows.remove(win.mChildWindows.get(j));
+ }
+ }
+ return NW > 0;
+ }
+
+ void dumpAppTokensLocked() {
+ for (int i=mAppTokens.size()-1; i>=0; i--) {
+ Log.v(TAG, " #" + i + ": " + mAppTokens.get(i).token);
+ }
+ }
+
+ void dumpWindowsLocked() {
+ for (int i=mWindows.size()-1; i>=0; i--) {
+ Log.v(TAG, " #" + i + ": " + mWindows.get(i));
+ }
+ }
+
+ private int findWindowOffsetLocked(int tokenPos) {
+ final int NW = mWindows.size();
+
+ if (tokenPos >= mAppTokens.size()) {
+ int i = NW;
+ while (i > 0) {
+ i--;
+ WindowState win = (WindowState)mWindows.get(i);
+ if (win.getAppToken() != null) {
+ return i+1;
+ }
+ }
+ }
+
+ while (tokenPos > 0) {
+ // Find the first app token below the new position that has
+ // a window displayed.
+ final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
+ if (DEBUG_REORDER) Log.v(TAG, "Looking for lower windows @ "
+ + tokenPos + " -- " + wtoken.token);
+ int i = wtoken.windows.size();
+ while (i > 0) {
+ i--;
+ WindowState win = wtoken.windows.get(i);
+ int j = win.mChildWindows.size();
+ while (j > 0) {
+ j--;
+ WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ if (cwin.mSubLayer >= 0 ) {
+ for (int pos=NW-1; pos>=0; pos--) {
+ if (mWindows.get(pos) == cwin) {
+ if (DEBUG_REORDER) Log.v(TAG,
+ "Found child win @" + (pos+1));
+ return pos+1;
+ }
+ }
+ }
+ }
+ for (int pos=NW-1; pos>=0; pos--) {
+ if (mWindows.get(pos) == win) {
+ if (DEBUG_REORDER) Log.v(TAG, "Found win @" + (pos+1));
+ return pos+1;
+ }
+ }
+ }
+ tokenPos--;
+ }
+
+ return 0;
+ }
+
+ private final int reAddAppWindowsLocked(int index, WindowToken token) {
+ final int NW = token.windows.size();
+ for (int i=0; i<NW; i++) {
+ WindowState win = token.windows.get(i);
+ final int NCW = win.mChildWindows.size();
+ boolean added = false;
+ for (int j=0; j<NCW; j++) {
+ WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ if (cwin.mSubLayer >= 0) {
+ mWindows.add(index, win);
+ index++;
+ added = true;
+ }
+ mWindows.add(index, cwin);
+ index++;
+ }
+ if (!added) {
+ mWindows.add(index, win);
+ index++;
+ }
+ }
+ return index;
+ }
+
+ public void moveAppToken(int index, IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "moveAppToken()")) {
+ return;
+ }
+
+ synchronized(mWindowMap) {
+ if (DEBUG_REORDER) Log.v(TAG, "Initial app tokens:");
+ if (DEBUG_REORDER) dumpAppTokensLocked();
+ final AppWindowToken wtoken = findAppWindowToken(token);
+ if (wtoken == null || !mAppTokens.remove(wtoken)) {
+ Log.w(TAG, "Attempting to reorder token that doesn't exist: "
+ + token + " (" + wtoken + ")");
+ return;
+ }
+ mAppTokens.add(index, wtoken);
+ if (DEBUG_REORDER) Log.v(TAG, "Moved " + token + " to " + index + ":");
+ if (DEBUG_REORDER) dumpAppTokensLocked();
+
+ final long origId = Binder.clearCallingIdentity();
+ if (DEBUG_REORDER) Log.v(TAG, "Removing windows in " + token + ":");
+ if (DEBUG_REORDER) dumpWindowsLocked();
+ if (tmpRemoveAppWindowsLocked(wtoken)) {
+ if (DEBUG_REORDER) Log.v(TAG, "Adding windows back in:");
+ if (DEBUG_REORDER) dumpWindowsLocked();
+ reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);
+ if (DEBUG_REORDER) Log.v(TAG, "Final window list:");
+ if (DEBUG_REORDER) dumpWindowsLocked();
+ assignLayersLocked();
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ updateFocusedWindowLocked();
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private void removeAppTokensLocked(List<IBinder> tokens) {
+ // XXX This should be done more efficiently!
+ // (take advantage of the fact that both lists should be
+ // ordered in the same way.)
+ int N = tokens.size();
+ for (int i=0; i<N; i++) {
+ IBinder token = tokens.get(i);
+ final AppWindowToken wtoken = findAppWindowToken(token);
+ if (!mAppTokens.remove(wtoken)) {
+ Log.w(TAG, "Attempting to reorder token that doesn't exist: "
+ + token + " (" + wtoken + ")");
+ i--;
+ N--;
+ }
+ }
+ }
+
+ private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
+ // First remove all of the windows from the list.
+ final int N = tokens.size();
+ int i;
+ for (i=0; i<N; i++) {
+ WindowToken token = mTokenMap.get(tokens.get(i));
+ if (token != null) {
+ tmpRemoveAppWindowsLocked(token);
+ }
+ }
+
+ // Where to start adding?
+ int pos = findWindowOffsetLocked(tokenPos);
+
+ // And now add them back at the correct place.
+ for (i=0; i<N; i++) {
+ WindowToken token = mTokenMap.get(tokens.get(i));
+ if (token != null) {
+ pos = reAddAppWindowsLocked(pos, token);
+ }
+ }
+
+ assignLayersLocked();
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ updateFocusedWindowLocked();
+
+ //dump();
+ }
+
+ public void moveAppTokensToTop(List<IBinder> tokens) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "moveAppTokensToTop()")) {
+ return;
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ synchronized(mWindowMap) {
+ removeAppTokensLocked(tokens);
+ final int N = tokens.size();
+ for (int i=0; i<N; i++) {
+ AppWindowToken wt = findAppWindowToken(tokens.get(i));
+ if (wt != null) {
+ mAppTokens.add(wt);
+ }
+ }
+ moveAppWindowsLocked(tokens, mAppTokens.size());
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ public void moveAppTokensToBottom(List<IBinder> tokens) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "moveAppTokensToBottom()")) {
+ return;
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ synchronized(mWindowMap) {
+ removeAppTokensLocked(tokens);
+ final int N = tokens.size();
+ int pos = 0;
+ for (int i=0; i<N; i++) {
+ AppWindowToken wt = findAppWindowToken(tokens.get(i));
+ if (wt != null) {
+ mAppTokens.add(pos, wt);
+ pos++;
+ }
+ }
+ moveAppWindowsLocked(tokens, 0);
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ // -------------------------------------------------------------
+ // Misc IWindowSession methods
+ // -------------------------------------------------------------
+
+ public void disableKeyguard(IBinder token, String tag) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+ }
+ mKeyguardDisabled.acquire(token, tag);
+ }
+
+ public void reenableKeyguard(IBinder token) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+ }
+ synchronized (mKeyguardDisabled) {
+ mKeyguardDisabled.release(token);
+
+ if (!mKeyguardDisabled.isAcquired()) {
+ // if we are the last one to reenable the keyguard wait until
+ // we have actaully finished reenabling until returning
+ mWaitingUntilKeyguardReenabled = true;
+ while (mWaitingUntilKeyguardReenabled) {
+ try {
+ mKeyguardDisabled.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @see android.app.KeyguardManager#exitKeyguardSecurely
+ */
+ public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+ }
+ mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() {
+ public void onKeyguardExitResult(boolean success) {
+ try {
+ callback.onKeyguardExitResult(success);
+ } catch (RemoteException e) {
+ // Client has died, we don't care.
+ }
+ }
+ });
+ }
+
+ public boolean inKeyguardRestrictedInputMode() {
+ return mPolicy.inKeyguardRestrictedKeyInputMode();
+ }
+
+ static float fixScale(float scale) {
+ if (scale < 0) scale = 0;
+ else if (scale > 20) scale = 20;
+ return Math.abs(scale);
+ }
+
+ public void setAnimationScale(int which, float scale) {
+ if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
+ "setAnimationScale()")) {
+ return;
+ }
+
+ if (scale < 0) scale = 0;
+ else if (scale > 20) scale = 20;
+ scale = Math.abs(scale);
+ switch (which) {
+ case 0: mWindowAnimationScale = fixScale(scale); break;
+ case 1: mTransitionAnimationScale = fixScale(scale); break;
+ }
+
+ // Persist setting
+ mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
+ }
+
+ public void setAnimationScales(float[] scales) {
+ if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
+ "setAnimationScale()")) {
+ return;
+ }
+
+ if (scales != null) {
+ if (scales.length >= 1) {
+ mWindowAnimationScale = fixScale(scales[0]);
+ }
+ if (scales.length >= 2) {
+ mTransitionAnimationScale = fixScale(scales[1]);
+ }
+ }
+
+ // Persist setting
+ mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
+ }
+
+ public float getAnimationScale(int which) {
+ switch (which) {
+ case 0: return mWindowAnimationScale;
+ case 1: return mTransitionAnimationScale;
+ }
+ return 0;
+ }
+
+ public float[] getAnimationScales() {
+ return new float[] { mWindowAnimationScale, mTransitionAnimationScale };
+ }
+
+ public int getSwitchState(int sw) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "getSwitchState()")) {
+ return -1;
+ }
+ return KeyInputQueue.getSwitchState(sw);
+ }
+
+ public int getSwitchStateForDevice(int devid, int sw) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "getSwitchStateForDevice()")) {
+ return -1;
+ }
+ return KeyInputQueue.getSwitchState(devid, sw);
+ }
+
+ public int getScancodeState(int sw) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "getScancodeState()")) {
+ return -1;
+ }
+ return KeyInputQueue.getScancodeState(sw);
+ }
+
+ public int getScancodeStateForDevice(int devid, int sw) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "getScancodeStateForDevice()")) {
+ return -1;
+ }
+ return KeyInputQueue.getScancodeState(devid, sw);
+ }
+
+ public int getKeycodeState(int sw) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "getKeycodeState()")) {
+ return -1;
+ }
+ return KeyInputQueue.getKeycodeState(sw);
+ }
+
+ public int getKeycodeStateForDevice(int devid, int sw) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "getKeycodeStateForDevice()")) {
+ return -1;
+ }
+ return KeyInputQueue.getKeycodeState(devid, sw);
+ }
+
+ public void enableScreenAfterBoot() {
+ synchronized(mWindowMap) {
+ if (mSystemBooted) {
+ return;
+ }
+ mSystemBooted = true;
+ }
+
+ performEnableScreen();
+ }
+
+ public void enableScreenIfNeededLocked() {
+ if (mDisplayEnabled) {
+ return;
+ }
+ if (!mSystemBooted) {
+ return;
+ }
+ mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN));
+ }
+
+ public void performEnableScreen() {
+ synchronized(mWindowMap) {
+ if (mDisplayEnabled) {
+ return;
+ }
+ if (!mSystemBooted) {
+ return;
+ }
+
+ // Don't enable the screen until all existing windows
+ // have been drawn.
+ final int N = mWindows.size();
+ for (int i=0; i<N; i++) {
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.isVisible() && !w.isDisplayedLw()) {
+ return;
+ }
+ }
+
+ mDisplayEnabled = true;
+ if (false) {
+ Log.i(TAG, "ENABLING SCREEN!");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ this.dump(null, pw, null);
+ Log.i(TAG, sw.toString());
+ }
+ try {
+ IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+ if (surfaceFlinger != null) {
+ //Log.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION,
+ data, null, 0);
+ data.recycle();
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Boot completed: SurfaceFlinger is dead!");
+ }
+ }
+
+ mPolicy.enableScreenAfterBoot();
+
+ // Make sure the last requested orientation has been applied.
+ setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false);
+ }
+
+ public void setInTouchMode(boolean mode) {
+ synchronized(mWindowMap) {
+ mInTouchMode = mode;
+ }
+ }
+
+ public void setRotation(int rotation,
+ boolean alwaysSendConfiguration) {
+ if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+ "setOrientation()")) {
+ return;
+ }
+
+ setRotationUnchecked(rotation, alwaysSendConfiguration);
+ }
+
+ public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration) {
+ if(DEBUG_ORIENTATION) Log.v(TAG,
+ "alwaysSendConfiguration set to "+alwaysSendConfiguration);
+
+ long origId = Binder.clearCallingIdentity();
+ boolean changed;
+ synchronized(mWindowMap) {
+ changed = setRotationUncheckedLocked(rotation);
+ }
+
+ if (changed) {
+ sendNewConfiguration();
+ synchronized(mWindowMap) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ } else if (alwaysSendConfiguration) {
+ //update configuration ignoring orientation change
+ sendNewConfiguration();
+ }
+
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ public boolean setRotationUncheckedLocked(int rotation) {
+ boolean changed;
+ if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) {
+ rotation = mRequestedRotation;
+ } else {
+ mRequestedRotation = rotation;
+ }
+ if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from "+rotation);
+ rotation = mPolicy.rotationForOrientation(mForcedAppOrientation);
+ if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to "+rotation);
+ changed = mDisplayEnabled && mRotation != rotation;
+
+ if (changed) {
+ mRotation = rotation;
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Rotation changed to " + rotation
+ + " from " + mRotation
+ + " (forceApp=" + mForcedAppOrientation
+ + ", req=" + mRequestedRotation + ")");
+ mWindowsFreezingScreen = true;
+ mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
+ 2000);
+ startFreezingDisplayLocked();
+ mQueue.setOrientation(rotation);
+ if (mDisplayEnabled) {
+ Surface.setOrientation(0, rotation);
+ }
+ for (int i=mWindows.size()-1; i>=0; i--) {
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.mSurface != null) {
+ w.mOrientationChanging = true;
+ }
+ }
+ for (int i=mRotationWatchers.size()-1; i>=0; i--) {
+ try {
+ mRotationWatchers.get(i).onRotationChanged(rotation);
+ } catch (RemoteException e) {
+ }
+ }
+ } //end if changed
+
+ return changed;
+ }
+
+ public int getRotation() {
+ return mRotation;
+ }
+
+ public int watchRotation(IRotationWatcher watcher) {
+ final IBinder watcherBinder = watcher.asBinder();
+ IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
+ public void binderDied() {
+ synchronized (mWindowMap) {
+ for (int i=0; i<mRotationWatchers.size(); i++) {
+ if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
+ mRotationWatchers.remove(i);
+ i--;
+ }
+ }
+ }
+ }
+ };
+
+ synchronized (mWindowMap) {
+ try {
+ watcher.asBinder().linkToDeath(dr, 0);
+ mRotationWatchers.add(watcher);
+ } catch (RemoteException e) {
+ // Client died, no cleanup needed.
+ }
+
+ return mRotation;
+ }
+ }
+
+ /**
+ * 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.ViewServer
+ * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT
+ */
+ public boolean startViewServer(int port) {
+ if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+ 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) {
+ Log.w(TAG, "View server did not start");
+ }
+ }
+ return false;
+ }
+
+ try {
+ mViewServer = new ViewServer(this, port);
+ return mViewServer.start();
+ } catch (IOException e) {
+ Log.w(TAG, "View server did not start");
+ }
+ return false;
+ }
+
+ /**
+ * 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.ViewServer
+ */
+ public boolean stopViewServer() {
+ if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+ 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.ViewServer
+ */
+ public boolean isViewServerRunning() {
+ if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+ 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 ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+ return false;
+ }
+
+ boolean result = true;
+
+ Object[] windows;
+ synchronized (mWindowMap) {
+ windows = new Object[mWindows.size()];
+ //noinspection unchecked
+ windows = mWindows.toArray(windows);
+ }
+
+ 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.length;
+ for (int i = 0; i < count; i++) {
+ final WindowState w = (WindowState) windows[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;
+ }
+
+ /**
+ * 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 ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+ return false;
+ }
+
+ boolean success = true;
+ Parcel data = null;
+ Parcel reply = 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 = "ffffffff".equals(code) ? -1 : Integer.parseInt(code, 16);
+
+ // Extract the command's parameter after the window description
+ if (index < parameters.length()) {
+ parameters = parameters.substring(index + 1);
+ } else {
+ parameters = "";
+ }
+
+ final WindowManagerService.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();
+
+ } catch (Exception e) {
+ Log.w(TAG, "Could not send command " + command + " with parameters " + parameters, e);
+ success = false;
+ } finally {
+ if (data != null) {
+ data.recycle();
+ }
+ if (reply != null) {
+ reply.recycle();
+ }
+ }
+
+ return success;
+ }
+
+ private WindowState findWindow(int hashCode) {
+ if (hashCode == -1) {
+ return getFocusedWindow();
+ }
+
+ synchronized (mWindowMap) {
+ final ArrayList windows = mWindows;
+ final int count = windows.size();
+
+ for (int i = 0; i < count; i++) {
+ WindowState w = (WindowState) windows.get(i);
+ if (System.identityHashCode(w) == hashCode) {
+ return w;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ void sendNewConfiguration() {
+ Configuration config;
+ synchronized (mWindowMap) {
+ config = computeNewConfigurationLocked();
+ }
+
+ if (config != null) {
+ try {
+ mActivityManager.updateConfiguration(config);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ Configuration computeNewConfigurationLocked() {
+ synchronized (mWindowMap) {
+ if (mDisplay == null) {
+ return null;
+ }
+ Configuration config = new Configuration();
+ mQueue.getInputConfiguration(config);
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+ int orientation = Configuration.ORIENTATION_SQUARE;
+ if (dw < dh) {
+ orientation = Configuration.ORIENTATION_PORTRAIT;
+ } else if (dw > dh) {
+ orientation = Configuration.ORIENTATION_LANDSCAPE;
+ }
+ config.orientation = orientation;
+ config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
+ mPolicy.adjustConfigurationLw(config);
+ Log.i(TAG, "Input configuration changed: " + config);
+ long now = SystemClock.uptimeMillis();
+ //Log.i(TAG, "Config changing, gc pending: " + mFreezeGcPending + ", now " + now);
+ if (mFreezeGcPending != 0) {
+ if (now > (mFreezeGcPending+1000)) {
+ //Log.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000));
+ mH.removeMessages(H.FORCE_GC);
+ Runtime.getRuntime().gc();
+ mFreezeGcPending = now;
+ }
+ } else {
+ mFreezeGcPending = now;
+ }
+ return config;
+ }
+ }
+
+ // -------------------------------------------------------------
+ // Input Events and Focus Management
+ // -------------------------------------------------------------
+
+ private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
+ if (targetWin == null ||
+ targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false, eventType);
+ }
+ }
+
+ // tells if it's a cheek event or not -- this function is stateful
+ private static final int EVENT_NONE = 0;
+ private static final int EVENT_UNKNOWN = 0;
+ private static final int EVENT_CHEEK = 0;
+ private static final int EVENT_IGNORE_DURATION = 300; // ms
+ private static final float CHEEK_THRESHOLD = 0.6f;
+ private int mEventState = EVENT_NONE;
+ private float mEventSize;
+ private int eventType(MotionEvent ev) {
+ float size = ev.getSize();
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mEventSize = size;
+ return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_EVENT;
+ case MotionEvent.ACTION_UP:
+ if (size > mEventSize) mEventSize = size;
+ return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : OTHER_EVENT;
+ case MotionEvent.ACTION_MOVE:
+ final int N = ev.getHistorySize();
+ if (size > mEventSize) mEventSize = size;
+ if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
+ for (int i=0; i<N; i++) {
+ size = ev.getHistoricalSize(i);
+ if (size > mEventSize) mEventSize = size;
+ if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
+ }
+ if (ev.getEventTime() < ev.getDownTime() + EVENT_IGNORE_DURATION) {
+ return TOUCH_EVENT;
+ } else {
+ return OTHER_EVENT;
+ }
+ default:
+ // not good
+ return OTHER_EVENT;
+ }
+ }
+
+ /**
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ private boolean dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
+ if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG,
+ "dispatchPointer " + ev);
+
+ Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
+ ev, true, false);
+
+ int action = ev.getAction();
+
+ if (action == MotionEvent.ACTION_UP) {
+ // let go of our target
+ mKeyWaiter.mMotionTarget = null;
+ mPowerManager.logPointerUpEvent();
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ mPowerManager.logPointerDownEvent();
+ }
+
+ if (targetObj == null) {
+ // In this case we are either dropping the event, or have received
+ // a move or up without a down. It is common to receive move
+ // events in such a way, since this means the user is moving the
+ // pointer without actually pressing down. All other cases should
+ // be atypical, so let's log them.
+ if (ev.getAction() != MotionEvent.ACTION_MOVE) {
+ Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
+ }
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return false;
+ }
+ if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return true;
+ }
+
+ WindowState target = (WindowState)targetObj;
+
+ final long eventTime = ev.getEventTime();
+
+ //Log.i(TAG, "Sending " + ev + " to " + target);
+
+ if (uid != 0 && uid != target.mSession.mUid) {
+ if (mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, pid, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Permission denied: injecting pointer event from pid "
+ + pid + " uid " + uid + " to window " + target
+ + " owned by uid " + target.mSession.mUid);
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return false;
+ }
+ }
+
+ if ((target.mAttrs.flags &
+ WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
+ //target wants to ignore fat touch events
+ boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev);
+ //explicit flag to return without processing event further
+ boolean returnFlag = false;
+ if((action == MotionEvent.ACTION_DOWN)) {
+ mFatTouch = false;
+ if(cheekPress) {
+ mFatTouch = true;
+ returnFlag = true;
+ }
+ } else {
+ if(action == MotionEvent.ACTION_UP) {
+ if(mFatTouch) {
+ //earlier even was invalid doesnt matter if current up is cheekpress or not
+ mFatTouch = false;
+ returnFlag = true;
+ } else if(cheekPress) {
+ //cancel the earlier event
+ ev.setAction(MotionEvent.ACTION_CANCEL);
+ action = MotionEvent.ACTION_CANCEL;
+ }
+ } else if(action == MotionEvent.ACTION_MOVE) {
+ if(mFatTouch) {
+ //two cases here
+ //an invalid down followed by 0 or moves(valid or invalid)
+ //a valid down, invalid move, more moves. want to ignore till up
+ returnFlag = true;
+ } else if(cheekPress) {
+ //valid down followed by invalid moves
+ //an invalid move have to cancel earlier action
+ ev.setAction(MotionEvent.ACTION_CANCEL);
+ action = MotionEvent.ACTION_CANCEL;
+ if (DEBUG_INPUT) Log.v(TAG, "Sending cancel for invalid ACTION_MOVE");
+ //note that the subsequent invalid moves will not get here
+ mFatTouch = true;
+ }
+ }
+ } //else if action
+ if(returnFlag) {
+ //recycle que, ev
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return false;
+ }
+ } //end if target
+
+ synchronized(mWindowMap) {
+ if (qev != null && action == MotionEvent.ACTION_MOVE) {
+ mKeyWaiter.bindTargetWindowLocked(target,
+ KeyWaiter.RETURN_PENDING_POINTER, qev);
+ ev = null;
+ } else {
+ final Rect frame = target.mFrame;
+ ev.offsetLocation(-(float)frame.left, -(float)frame.top);
+ mKeyWaiter.bindTargetWindowLocked(target);
+ }
+ }
+
+ // finally offset the event to the target's coordinate system and
+ // dispatch the event.
+ try {
+ if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
+ Log.v(TAG, "Delivering pointer " + qev + " to " + target);
+ }
+ target.mClient.dispatchPointer(ev, eventTime);
+ return true;
+ } catch (android.os.RemoteException e) {
+ Log.i(TAG, "WINDOW DIED during motion dispatch: " + target);
+ mKeyWaiter.mMotionTarget = null;
+ try {
+ removeWindow(target.mSession, target.mClient);
+ } catch (java.util.NoSuchElementException ex) {
+ // This will happen if the window has already been
+ // removed.
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ private boolean dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
+ if (DEBUG_INPUT) Log.v(
+ TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
+
+ Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
+ ev, false, false);
+ if (focusObj == null) {
+ Log.w(TAG, "No focus window, dropping trackball: " + ev);
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return false;
+ }
+ if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return true;
+ }
+
+ WindowState focus = (WindowState)focusObj;
+
+ if (uid != 0 && uid != focus.mSession.mUid) {
+ if (mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, pid, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Permission denied: injecting key event from pid "
+ + pid + " uid " + uid + " to window " + focus
+ + " owned by uid " + focus.mSession.mUid);
+ if (qev != null) {
+ mQueue.recycleEvent(qev);
+ }
+ ev.recycle();
+ return false;
+ }
+ }
+
+ final long eventTime = ev.getEventTime();
+
+ synchronized(mWindowMap) {
+ if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) {
+ mKeyWaiter.bindTargetWindowLocked(focus,
+ KeyWaiter.RETURN_PENDING_TRACKBALL, qev);
+ // We don't deliver movement events to the client, we hold
+ // them and wait for them to call back.
+ ev = null;
+ } else {
+ mKeyWaiter.bindTargetWindowLocked(focus);
+ }
+ }
+
+ try {
+ focus.mClient.dispatchTrackball(ev, eventTime);
+ return true;
+ } catch (android.os.RemoteException e) {
+ Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
+ try {
+ removeWindow(focus.mSession, focus.mClient);
+ } catch (java.util.NoSuchElementException ex) {
+ // This will happen if the window has already been
+ // removed.
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ private boolean dispatchKey(KeyEvent event, int pid, int uid) {
+ if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
+
+ Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
+ null, false, false);
+ if (focusObj == null) {
+ Log.w(TAG, "No focus window, dropping: " + event);
+ return false;
+ }
+ if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+ return true;
+ }
+
+ WindowState focus = (WindowState)focusObj;
+
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Dispatching to " + focus + ": " + event);
+
+ if (uid != 0 && uid != focus.mSession.mUid) {
+ if (mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, pid, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Permission denied: injecting key event from pid "
+ + pid + " uid " + uid + " to window " + focus
+ + " owned by uid " + focus.mSession.mUid);
+ return false;
+ }
+ }
+
+ synchronized(mWindowMap) {
+ mKeyWaiter.bindTargetWindowLocked(focus);
+ }
+
+ try {
+ if (DEBUG_INPUT || DEBUG_FOCUS) {
+ Log.v(TAG, "Delivering key " + event.getKeyCode()
+ + " to " + focus);
+ }
+ focus.mClient.dispatchKey(event);
+ return true;
+ } catch (android.os.RemoteException e) {
+ Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
+ try {
+ removeWindow(focus.mSession, focus.mClient);
+ } catch (java.util.NoSuchElementException ex) {
+ // This will happen if the window has already been
+ // removed.
+ }
+ }
+
+ return false;
+ }
+
+ public void pauseKeyDispatching(IBinder _token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "pauseKeyDispatching()")) {
+ return;
+ }
+
+ synchronized (mWindowMap) {
+ WindowToken token = mTokenMap.get(_token);
+ if (token != null) {
+ mKeyWaiter.pauseDispatchingLocked(token);
+ }
+ }
+ }
+
+ public void resumeKeyDispatching(IBinder _token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "resumeKeyDispatching()")) {
+ return;
+ }
+
+ synchronized (mWindowMap) {
+ WindowToken token = mTokenMap.get(_token);
+ if (token != null) {
+ mKeyWaiter.resumeDispatchingLocked(token);
+ }
+ }
+ }
+
+ public void setEventDispatching(boolean enabled) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "resumeKeyDispatching()")) {
+ return;
+ }
+
+ synchronized (mWindowMap) {
+ mKeyWaiter.setEventDispatchingLocked(enabled);
+ }
+ }
+
+ /**
+ * Injects a keystroke event into the UI.
+ *
+ * @param ev A motion event describing the keystroke action. (Be sure to use
+ * {@link SystemClock#uptimeMillis()} as the timebase.)
+ * @param sync If true, wait for the event to be completed before returning to the caller.
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ public boolean injectKeyEvent(KeyEvent ev, boolean sync) {
+ long downTime = ev.getDownTime();
+ long eventTime = ev.getEventTime();
+
+ int action = ev.getAction();
+ int code = ev.getKeyCode();
+ int repeatCount = ev.getRepeatCount();
+ int metaState = ev.getMetaState();
+ int deviceId = ev.getDeviceId();
+ int scancode = ev.getScanCode();
+
+ if (eventTime == 0) eventTime = SystemClock.uptimeMillis();
+ if (downTime == 0) downTime = eventTime;
+
+ KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
+ deviceId, scancode);
+
+ boolean result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+ if (sync) {
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ }
+ return result;
+ }
+
+ /**
+ * Inject a pointer (touch) event into the UI.
+ *
+ * @param ev A motion event describing the trackball action. (As noted in
+ * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
+ * {@link SystemClock#uptimeMillis()} as the timebase.)
+ * @param sync If true, wait for the event to be completed before returning to the caller.
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
+ boolean result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+ if (sync) {
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ }
+ return result;
+ }
+
+ /**
+ * Inject a trackball (navigation device) event into the UI.
+ *
+ * @param ev A motion event describing the trackball action. (As noted in
+ * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
+ * {@link SystemClock#uptimeMillis()} as the timebase.)
+ * @param sync If true, wait for the event to be completed before returning to the caller.
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
+ boolean result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+ if (sync) {
+ mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+ }
+ return result;
+ }
+
+ private WindowState getFocusedWindow() {
+ synchronized (mWindowMap) {
+ return getFocusedWindowLocked();
+ }
+ }
+
+ private WindowState getFocusedWindowLocked() {
+ return mCurrentFocus;
+ }
+
+ /**
+ * This class holds the state for dispatching key events. This state
+ * is protected by the KeyWaiter instance, NOT by the window lock. You
+ * can be holding the main window lock while acquire the KeyWaiter lock,
+ * but not the other way around.
+ */
+ final class KeyWaiter {
+ public static final int RETURN_NOTHING = 0;
+ public static final int RETURN_PENDING_POINTER = 1;
+ public static final int RETURN_PENDING_TRACKBALL = 2;
+
+ final Object SKIP_TARGET_TOKEN = new Object();
+ final Object CONSUMED_EVENT_TOKEN = new Object();
+
+ private WindowState mLastWin = null;
+ private IBinder mLastBinder = null;
+ private boolean mFinished = true;
+ private boolean mGotFirstWindow = false;
+ private boolean mEventDispatching = true;
+ private long mTimeToSwitch = 0;
+ /* package */ boolean mWasFrozen = false;
+
+ // Target of Motion events
+ WindowState mMotionTarget;
+
+ /**
+ * Wait for the last event dispatch to complete, then find the next
+ * target that should receive the given event and wait for that one
+ * to be ready to receive it.
+ */
+ Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
+ MotionEvent nextMotion, boolean isPointerEvent,
+ boolean failIfTimeout) {
+ long startTime = SystemClock.uptimeMillis();
+ long keyDispatchingTimeout = 5 * 1000;
+ long waitedFor = 0;
+
+ while (true) {
+ // Figure out which window we care about. It is either the
+ // last window we are waiting to have process the event or,
+ // if none, then the next window we think the event should go
+ // to. Note: we retrieve mLastWin outside of the lock, so
+ // it may change before we lock. Thus we must check it again.
+ WindowState targetWin = mLastWin;
+ boolean targetIsNew = targetWin == null;
+ if (DEBUG_INPUT) Log.v(
+ TAG, "waitForLastKey: mFinished=" + mFinished +
+ ", mLastWin=" + mLastWin);
+ if (targetIsNew) {
+ Object target = findTargetWindow(nextKey, qev, nextMotion,
+ isPointerEvent);
+ if (target == SKIP_TARGET_TOKEN) {
+ // The user has pressed a special key, and we are
+ // dropping all pending events before it.
+ if (DEBUG_INPUT) Log.v(TAG, "Skipping: " + nextKey
+ + " " + nextMotion);
+ return null;
+ }
+ if (target == CONSUMED_EVENT_TOKEN) {
+ if (DEBUG_INPUT) Log.v(TAG, "Consumed: " + nextKey
+ + " " + nextMotion);
+ return target;
+ }
+ targetWin = (WindowState)target;
+ }
+
+ AppWindowToken targetApp = null;
+
+ // Now: is it okay to send the next event to this window?
+ synchronized (this) {
+ // First: did we come here based on the last window not
+ // being null, but it changed by the time we got here?
+ // If so, try again.
+ if (!targetIsNew && mLastWin == null) {
+ continue;
+ }
+
+ // We never dispatch events if not finished with the
+ // last one, or the display is frozen.
+ if (mFinished && !mDisplayFrozen) {
+ // If event dispatching is disabled, then we
+ // just consume the events.
+ if (!mEventDispatching) {
+ if (DEBUG_INPUT) Log.v(TAG,
+ "Skipping event; dispatching disabled: "
+ + nextKey + " " + nextMotion);
+ return null;
+ }
+ if (targetWin != null) {
+ // If this is a new target, and that target is not
+ // paused or unresponsive, then all looks good to
+ // handle the event.
+ if (targetIsNew && !targetWin.mToken.paused) {
+ return targetWin;
+ }
+
+ // If we didn't find a target window, and there is no
+ // focused app window, then just eat the events.
+ } else if (mFocusedApp == null) {
+ if (DEBUG_INPUT) Log.v(TAG,
+ "Skipping event; no focused app: "
+ + nextKey + " " + nextMotion);
+ return null;
+ }
+ }
+
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Waiting for last key in " + mLastBinder
+ + " target=" + targetWin
+ + " mFinished=" + mFinished
+ + " mDisplayFrozen=" + mDisplayFrozen
+ + " targetIsNew=" + targetIsNew
+ + " paused="
+ + (targetWin != null ? targetWin.mToken.paused : false)
+ + " mFocusedApp=" + mFocusedApp);
+
+ targetApp = targetWin != null
+ ? targetWin.mAppToken : mFocusedApp;
+
+ long curTimeout = keyDispatchingTimeout;
+ if (mTimeToSwitch != 0) {
+ long now = SystemClock.uptimeMillis();
+ if (mTimeToSwitch <= now) {
+ // If an app switch key has been pressed, and we have
+ // waited too long for the current app to finish
+ // processing keys, then wait no more!
+ doFinishedKeyLocked(true);
+ continue;
+ }
+ long switchTimeout = mTimeToSwitch - now;
+ if (curTimeout > switchTimeout) {
+ curTimeout = switchTimeout;
+ }
+ }
+
+ try {
+ // after that continue
+ // processing keys, so we don't get stuck.
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Waiting for key dispatch: " + curTimeout);
+ wait(curTimeout);
+ if (DEBUG_INPUT) Log.v(TAG, "Finished waiting @"
+ + SystemClock.uptimeMillis() + " startTime="
+ + startTime + " switchTime=" + mTimeToSwitch);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ // If we were frozen during configuration change, restart the
+ // timeout checks from now; otherwise look at whether we timed
+ // out before awakening.
+ if (mWasFrozen) {
+ waitedFor = 0;
+ mWasFrozen = false;
+ } else {
+ waitedFor = SystemClock.uptimeMillis() - startTime;
+ }
+
+ if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) {
+ IApplicationToken at = null;
+ synchronized (this) {
+ Log.w(TAG, "Key dispatching timed out sending to " +
+ (targetWin != null ? targetWin.mAttrs.getTitle()
+ : "<null>"));
+ //dump();
+ if (targetWin != null) {
+ at = targetWin.getAppToken();
+ } else if (targetApp != null) {
+ at = targetApp.appToken;
+ }
+ }
+
+ boolean abort = true;
+ if (at != null) {
+ try {
+ long timeout = at.getKeyDispatchingTimeout();
+ if (timeout > waitedFor) {
+ // we did not wait the proper amount of time for this application.
+ // set the timeout to be the real timeout and wait again.
+ keyDispatchingTimeout = timeout - waitedFor;
+ continue;
+ } else {
+ abort = at.keyDispatchingTimedOut();
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ synchronized (this) {
+ if (abort && (mLastWin == targetWin || targetWin == null)) {
+ mFinished = true;
+ if (mLastWin != null) {
+ if (DEBUG_INPUT) Log.v(TAG,
+ "Window " + mLastWin +
+ " timed out on key input");
+ if (mLastWin.mToken.paused) {
+ Log.w(TAG, "Un-pausing dispatching to this window");
+ mLastWin.mToken.paused = false;
+ }
+ }
+ if (mMotionTarget == targetWin) {
+ mMotionTarget = null;
+ }
+ mLastWin = null;
+ mLastBinder = null;
+ if (failIfTimeout || targetWin == null) {
+ return null;
+ }
+ } else {
+ Log.w(TAG, "Continuing to wait for key to be dispatched");
+ startTime = SystemClock.uptimeMillis();
+ }
+ }
+ }
+ }
+ }
+
+ Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
+ MotionEvent nextMotion, boolean isPointerEvent) {
+ if (nextKey != null) {
+ // Find the target window for a normal key event.
+ final int keycode = nextKey.getKeyCode();
+ final int repeatCount = nextKey.getRepeatCount();
+ final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
+ boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+ if (!dispatch) {
+ mPolicy.interceptKeyTi(null, keycode,
+ nextKey.getMetaState(), down, repeatCount);
+ Log.w(TAG, "Event timeout during app switch: dropping "
+ + nextKey);
+ return SKIP_TARGET_TOKEN;
+ }
+
+ // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")");
+
+ WindowState focus = null;
+ synchronized(mWindowMap) {
+ focus = getFocusedWindowLocked();
+ }
+
+ wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+
+ if (mPolicy.interceptKeyTi(focus,
+ keycode, nextKey.getMetaState(), down, repeatCount)) {
+ return CONSUMED_EVENT_TOKEN;
+ }
+
+ return focus;
+
+ } else if (!isPointerEvent) {
+ boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1);
+ if (!dispatch) {
+ Log.w(TAG, "Event timeout during app switch: dropping trackball "
+ + nextMotion);
+ return SKIP_TARGET_TOKEN;
+ }
+
+ WindowState focus = null;
+ synchronized(mWindowMap) {
+ focus = getFocusedWindowLocked();
+ }
+
+ wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+ return focus;
+ }
+
+ if (nextMotion == null) {
+ return SKIP_TARGET_TOKEN;
+ }
+
+ boolean dispatch = mKeyWaiter.checkShouldDispatchKey(
+ KeyEvent.KEYCODE_UNKNOWN);
+ if (!dispatch) {
+ Log.w(TAG, "Event timeout during app switch: dropping pointer "
+ + nextMotion);
+ return SKIP_TARGET_TOKEN;
+ }
+
+ // Find the target window for a pointer event.
+ int action = nextMotion.getAction();
+ final float xf = nextMotion.getX();
+ final float yf = nextMotion.getY();
+ final long eventTime = nextMotion.getEventTime();
+
+ final boolean screenWasOff = qev != null
+ && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
+
+ WindowState target = null;
+
+ synchronized(mWindowMap) {
+ synchronized (this) {
+ if (action == MotionEvent.ACTION_DOWN) {
+ if (mMotionTarget != null) {
+ // this is weird, we got a pen down, but we thought it was
+ // already down!
+ // XXX: We should probably send an ACTION_UP to the current
+ // target.
+ Log.w(TAG, "Pointer down received while already down in: "
+ + mMotionTarget);
+ mMotionTarget = null;
+ }
+
+ // ACTION_DOWN is special, because we need to lock next events to
+ // the window we'll land onto.
+ final int x = (int)xf;
+ final int y = (int)yf;
+
+ final ArrayList windows = mWindows;
+ final int N = windows.size();
+ WindowState topErrWindow = null;
+ for (int i=N-1; i>=0; i--) {
+ WindowState child = (WindowState)windows.get(i);
+ final int flags = child.mAttrs.flags;
+ if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
+ if (topErrWindow == null) {
+ topErrWindow = child;
+ }
+ }
+ if (!child.isVisible()) {
+ continue;
+ }
+ final Rect frame = child.mFrame;
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ continue;
+ }
+ final int touchFlags = flags &
+ (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ if (frame.contains(x, y) || (touchFlags == 0)) {
+ if (!screenWasOff || (flags &
+ WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
+ mMotionTarget = child;
+ } else {
+ mMotionTarget = null;
+ }
+ break;
+ }
+ }
+
+ // if there's an error window but it's not accepting
+ // focus (typically because it is not yet visible) just
+ // wait for it -- any other focused window may in fact
+ // be in ANR state.
+ if (topErrWindow != null && mMotionTarget != topErrWindow) {
+ mMotionTarget = null;
+ }
+ }
+
+ target = mMotionTarget;
+ }
+ }
+
+ wakeupIfNeeded(target, eventType(nextMotion));
+
+ // Pointer events are a little different -- if there isn't a
+ // target found for any event, then just drop it.
+ return target != null ? target : SKIP_TARGET_TOKEN;
+ }
+
+ boolean checkShouldDispatchKey(int keycode) {
+ synchronized (this) {
+ if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) {
+ mTimeToSwitch = 0;
+ return true;
+ }
+ if (mTimeToSwitch != 0
+ && mTimeToSwitch < SystemClock.uptimeMillis()) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ void bindTargetWindowLocked(WindowState win,
+ int pendingWhat, QueuedEvent pendingMotion) {
+ synchronized (this) {
+ bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion);
+ }
+ }
+
+ void bindTargetWindowLocked(WindowState win) {
+ synchronized (this) {
+ bindTargetWindowLockedLocked(win, RETURN_NOTHING, null);
+ }
+ }
+
+ void bindTargetWindowLockedLocked(WindowState win,
+ int pendingWhat, QueuedEvent pendingMotion) {
+ mLastWin = win;
+ mLastBinder = win.mClient.asBinder();
+ mFinished = false;
+ if (pendingMotion != null) {
+ final Session s = win.mSession;
+ if (pendingWhat == RETURN_PENDING_POINTER) {
+ releasePendingPointerLocked(s);
+ s.mPendingPointerMove = pendingMotion;
+ s.mPendingPointerWindow = win;
+ if (DEBUG_INPUT) Log.v(TAG,
+ "bindTargetToWindow " + s.mPendingPointerMove);
+ } else if (pendingWhat == RETURN_PENDING_TRACKBALL) {
+ releasePendingTrackballLocked(s);
+ s.mPendingTrackballMove = pendingMotion;
+ s.mPendingTrackballWindow = win;
+ }
+ }
+ }
+
+ void releasePendingPointerLocked(Session s) {
+ if (DEBUG_INPUT) Log.v(TAG,
+ "releasePendingPointer " + s.mPendingPointerMove);
+ if (s.mPendingPointerMove != null) {
+ mQueue.recycleEvent(s.mPendingPointerMove);
+ s.mPendingPointerMove = null;
+ }
+ }
+
+ void releasePendingTrackballLocked(Session s) {
+ if (s.mPendingTrackballMove != null) {
+ mQueue.recycleEvent(s.mPendingTrackballMove);
+ s.mPendingTrackballMove = null;
+ }
+ }
+
+ MotionEvent finishedKey(Session session, IWindow client, boolean force,
+ int returnWhat) {
+ if (DEBUG_INPUT) Log.v(
+ TAG, "finishedKey: client=" + client + ", force=" + force);
+
+ if (client == null) {
+ return null;
+ }
+
+ synchronized (this) {
+ if (DEBUG_INPUT) Log.v(
+ TAG, "finishedKey: client=" + client.asBinder()
+ + ", force=" + force + ", last=" + mLastBinder
+ + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
+
+ QueuedEvent qev = null;
+ WindowState win = null;
+ if (returnWhat == RETURN_PENDING_POINTER) {
+ qev = session.mPendingPointerMove;
+ win = session.mPendingPointerWindow;
+ session.mPendingPointerMove = null;
+ session.mPendingPointerWindow = null;
+ } else if (returnWhat == RETURN_PENDING_TRACKBALL) {
+ qev = session.mPendingTrackballMove;
+ win = session.mPendingTrackballWindow;
+ session.mPendingTrackballMove = null;
+ session.mPendingTrackballWindow = null;
+ }
+
+ if (mLastBinder == client.asBinder()) {
+ if (DEBUG_INPUT) Log.v(
+ TAG, "finishedKey: last paused="
+ + ((mLastWin != null) ? mLastWin.mToken.paused : "null"));
+ if (mLastWin != null && (!mLastWin.mToken.paused || force
+ || !mEventDispatching)) {
+ doFinishedKeyLocked(false);
+ } else {
+ // Make sure to wake up anyone currently waiting to
+ // dispatch a key, so they can re-evaluate their
+ // current situation.
+ mFinished = true;
+ notifyAll();
+ }
+ }
+
+ if (qev != null) {
+ MotionEvent res = (MotionEvent)qev.event;
+ if (DEBUG_INPUT) Log.v(TAG,
+ "Returning pending motion: " + res);
+ if (win != null && returnWhat == RETURN_PENDING_POINTER) {
+ res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
+ }
+ mQueue.recycleEvent(qev);
+ return res;
+ }
+ return null;
+ }
+ }
+
+ void tickle() {
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+
+ void handleNewWindowLocked(WindowState newWindow) {
+ if (!newWindow.canReceiveKeys()) {
+ return;
+ }
+ synchronized (this) {
+ if (DEBUG_INPUT) Log.v(
+ TAG, "New key dispatch window: win="
+ + newWindow.mClient.asBinder()
+ + ", last=" + mLastBinder
+ + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
+ + "), finished=" + mFinished + ", paused="
+ + newWindow.mToken.paused);
+
+ // Displaying a window implicitly causes dispatching to
+ // be unpaused. (This is to protect against bugs if someone
+ // pauses dispatching but forgets to resume.)
+ newWindow.mToken.paused = false;
+
+ mGotFirstWindow = true;
+ boolean doNotify = true;
+
+ if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
+ if (DEBUG_INPUT) Log.v(TAG,
+ "New SYSTEM_ERROR window; resetting state");
+ mLastWin = null;
+ mLastBinder = null;
+ mMotionTarget = null;
+ mFinished = true;
+ } else if (mLastWin != null) {
+ // If the new window is above the window we are
+ // waiting on, then stop waiting and let key dispatching
+ // start on the new guy.
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Last win layer=" + mLastWin.mLayer
+ + ", new win layer=" + newWindow.mLayer);
+ if (newWindow.mLayer >= mLastWin.mLayer) {
+ if (!mLastWin.canReceiveKeys()) {
+ mLastWin.mToken.paused = false;
+ doFinishedKeyLocked(true); // does a notifyAll()
+ doNotify = false;
+ }
+ } else {
+ // the new window is lower; no need to wake key waiters
+ doNotify = false;
+ }
+ }
+
+ if (doNotify) {
+ notifyAll();
+ }
+ }
+ }
+
+ void pauseDispatchingLocked(WindowToken token) {
+ synchronized (this)
+ {
+ if (DEBUG_INPUT) Log.v(TAG, "Pausing WindowToken " + token);
+ token.paused = true;
+
+ /*
+ if (mLastWin != null && !mFinished && mLastWin.mBaseLayer <= layer) {
+ mPaused = true;
+ } else {
+ if (mLastWin == null) {
+ if (Config.LOGI) Log.i(
+ TAG, "Key dispatching not paused: no last window.");
+ } else if (mFinished) {
+ if (Config.LOGI) Log.i(
+ TAG, "Key dispatching not paused: finished last key.");
+ } else {
+ if (Config.LOGI) Log.i(
+ TAG, "Key dispatching not paused: window in higher layer.");
+ }
+ }
+ */
+ }
+ }
+
+ void resumeDispatchingLocked(WindowToken token) {
+ synchronized (this) {
+ if (token.paused) {
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Resuming WindowToken " + token
+ + ", last=" + mLastBinder
+ + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
+ + "), finished=" + mFinished + ", paused="
+ + token.paused);
+ token.paused = false;
+ if (mLastWin != null && mLastWin.mToken == token && mFinished) {
+ doFinishedKeyLocked(true);
+ } else {
+ notifyAll();
+ }
+ }
+ }
+ }
+
+ void setEventDispatchingLocked(boolean enabled) {
+ synchronized (this) {
+ mEventDispatching = enabled;
+ notifyAll();
+ }
+ }
+
+ void appSwitchComing() {
+ synchronized (this) {
+ // Don't wait for more than .5 seconds for app to finish
+ // processing the pending events.
+ long now = SystemClock.uptimeMillis() + 500;
+ if (DEBUG_INPUT) Log.v(TAG, "appSwitchComing: " + now);
+ if (mTimeToSwitch == 0 || now < mTimeToSwitch) {
+ mTimeToSwitch = now;
+ }
+ notifyAll();
+ }
+ }
+
+ private final void doFinishedKeyLocked(boolean doRecycle) {
+ if (mLastWin != null) {
+ releasePendingPointerLocked(mLastWin.mSession);
+ releasePendingTrackballLocked(mLastWin.mSession);
+ }
+
+ if (mLastWin == null || !mLastWin.mToken.paused
+ || !mLastWin.isVisible()) {
+ // If the current window has been paused, we aren't -really-
+ // finished... so let the waiters still wait.
+ mLastWin = null;
+ mLastBinder = null;
+ }
+ mFinished = true;
+ notifyAll();
+ }
+ }
+
+ private class KeyQ extends KeyInputQueue
+ implements KeyInputQueue.FilterCallback {
+ PowerManager.WakeLock mHoldingScreen;
+
+ KeyQ() {
+ super(mContext);
+ PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+ | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG");
+ mHoldingScreen.setReferenceCounted(false);
+ }
+
+ @Override
+ boolean preprocessEvent(InputDevice device, RawInputEvent event) {
+ if (mPolicy.preprocessInputEventTq(event)) {
+ return true;
+ }
+
+ switch (event.type) {
+ case RawInputEvent.EV_KEY: {
+ // XXX begin hack
+ if (DEBUG) {
+ if (event.keycode == KeyEvent.KEYCODE_G) {
+ if (event.value != 0) {
+ // G down
+ mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+ }
+ return false;
+ }
+ if (event.keycode == KeyEvent.KEYCODE_D) {
+ if (event.value != 0) {
+ //dump();
+ }
+ return false;
+ }
+ }
+ // XXX end hack
+
+ boolean screenIsOff = !mPowerManager.screenIsOn();
+ boolean screenIsDim = !mPowerManager.screenIsBright();
+ int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
+
+ if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
+ mPowerManager.goToSleep(event.when);
+ }
+
+ if (screenIsOff) {
+ event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
+ }
+ if (screenIsDim) {
+ event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
+ }
+ if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
+ mPowerManager.userActivity(event.when, false,
+ LocalPowerManager.BUTTON_EVENT);
+ }
+
+ if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
+ if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
+ filterQueue(this);
+ mKeyWaiter.appSwitchComing();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ case RawInputEvent.EV_REL: {
+ boolean screenIsOff = !mPowerManager.screenIsOn();
+ boolean screenIsDim = !mPowerManager.screenIsBright();
+ if (screenIsOff) {
+ if (!mPolicy.isWakeRelMovementTq(event.deviceId,
+ device.classes, event)) {
+ //Log.i(TAG, "dropping because screenIsOff and !isWakeKey");
+ return false;
+ }
+ event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
+ }
+ if (screenIsDim) {
+ event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
+ }
+ return true;
+ }
+
+ case RawInputEvent.EV_ABS: {
+ boolean screenIsOff = !mPowerManager.screenIsOn();
+ boolean screenIsDim = !mPowerManager.screenIsBright();
+ if (screenIsOff) {
+ if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
+ device.classes, event)) {
+ //Log.i(TAG, "dropping because screenIsOff and !isWakeKey");
+ return false;
+ }
+ event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
+ }
+ if (screenIsDim) {
+ event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
+ }
+ return true;
+ }
+
+ default:
+ return true;
+ }
+ }
+
+ public int filterEvent(QueuedEvent ev) {
+ switch (ev.classType) {
+ case RawInputEvent.CLASS_KEYBOARD:
+ KeyEvent ke = (KeyEvent)ev.event;
+ if (mPolicy.isMovementKeyTi(ke.getKeyCode())) {
+ Log.w(TAG, "Dropping movement key during app switch: "
+ + ke.getKeyCode() + ", action=" + ke.getAction());
+ return FILTER_REMOVE;
+ }
+ return FILTER_ABORT;
+ default:
+ return FILTER_KEEP;
+ }
+ }
+
+ /**
+ * Must be called with the main window manager lock held.
+ */
+ void setHoldScreenLocked(boolean holding) {
+ boolean state = mHoldingScreen.isHeld();
+ if (holding != state) {
+ if (holding) {
+ mHoldingScreen.acquire();
+ } else {
+ long curTime = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT);
+ mHoldingScreen.release();
+ }
+ }
+ }
+ };
+
+ public void systemReady() {
+ mPolicy.systemReady();
+ }
+
+ private final class InputDispatcherThread extends Thread {
+ // Time to wait when there is nothing to do: 9999 seconds.
+ static final int LONG_WAIT=9999*1000;
+
+ public InputDispatcherThread() {
+ super("InputDispatcher");
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ process();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in input dispatcher", e);
+ }
+ }
+ }
+
+ private void process() {
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
+
+ // The last key event we saw
+ KeyEvent lastKey = null;
+
+ // Last keydown time for auto-repeating keys
+ long lastKeyTime = SystemClock.uptimeMillis();
+ long nextKeyTime = lastKeyTime+LONG_WAIT;
+
+ // How many successive repeats we generated
+ int keyRepeatCount = 0;
+
+ // Need to report that configuration has changed?
+ boolean configChanged = false;
+
+ while (true) {
+ long curTime = SystemClock.uptimeMillis();
+
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Waiting for next key: now=" + curTime
+ + ", repeat @ " + nextKeyTime);
+
+ // Retrieve next event, waiting only as long as the next
+ // repeat timeout. If the configuration has changed, then
+ // don't wait at all -- we'll report the change as soon as
+ // we have processed all events.
+ QueuedEvent ev = mQueue.getEvent(
+ (int)((!configChanged && curTime < nextKeyTime)
+ ? (nextKeyTime-curTime) : 0));
+
+ if (DEBUG_INPUT && ev != null) Log.v(
+ TAG, "Event: type=" + ev.classType + " data=" + ev.event);
+
+ try {
+ if (ev != null) {
+ curTime = ev.when;
+ int eventType;
+ if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
+ eventType = eventType((MotionEvent)ev.event);
+ } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
+ ev.classType == RawInputEvent.CLASS_TRACKBALL) {
+ eventType = LocalPowerManager.BUTTON_EVENT;
+ } else {
+ eventType = LocalPowerManager.OTHER_EVENT;
+ }
+ mPowerManager.userActivity(curTime, false, eventType);
+ switch (ev.classType) {
+ case RawInputEvent.CLASS_KEYBOARD:
+ KeyEvent ke = (KeyEvent)ev.event;
+ if (ke.isDown()) {
+ lastKey = ke;
+ keyRepeatCount = 0;
+ lastKeyTime = curTime;
+ nextKeyTime = lastKeyTime
+ + KEY_REPEAT_FIRST_DELAY;
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Received key down: first repeat @ "
+ + nextKeyTime);
+ } else {
+ lastKey = null;
+ // Arbitrary long timeout.
+ lastKeyTime = curTime;
+ nextKeyTime = curTime + LONG_WAIT;
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Received key up: ignore repeat @ "
+ + nextKeyTime);
+ }
+ dispatchKey((KeyEvent)ev.event, 0, 0);
+ mQueue.recycleEvent(ev);
+ break;
+ case RawInputEvent.CLASS_TOUCHSCREEN:
+ //Log.i(TAG, "Read next event " + ev);
+ dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
+ break;
+ case RawInputEvent.CLASS_TRACKBALL:
+ dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
+ break;
+ case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
+ configChanged = true;
+ break;
+ default:
+ mQueue.recycleEvent(ev);
+ break;
+ }
+
+ } else if (configChanged) {
+ configChanged = false;
+ sendNewConfiguration();
+
+ } else if (lastKey != null) {
+ curTime = SystemClock.uptimeMillis();
+
+ // Timeout occurred while key was down. If it is at or
+ // past the key repeat time, dispatch the repeat.
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Key timeout: repeat=" + nextKeyTime
+ + ", now=" + curTime);
+ if (curTime < nextKeyTime) {
+ continue;
+ }
+
+ lastKeyTime = nextKeyTime;
+ nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
+ keyRepeatCount++;
+ if (DEBUG_INPUT) Log.v(
+ TAG, "Key repeat: count=" + keyRepeatCount
+ + ", next @ " + nextKeyTime);
+ dispatchKey(new KeyEvent(lastKey, curTime, keyRepeatCount), 0, 0);
+
+ } else {
+ curTime = SystemClock.uptimeMillis();
+
+ lastKeyTime = curTime;
+ nextKeyTime = curTime + LONG_WAIT;
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG,
+ "Input thread received uncaught exception: " + e, e);
+ }
+ }
+ }
+ }
+
+ // -------------------------------------------------------------
+ // Client Session State
+ // -------------------------------------------------------------
+
+ private final class Session extends IWindowSession.Stub
+ implements IBinder.DeathRecipient {
+ final IBinder mToken;
+ final int mUid;
+ final int mPid;
+ SurfaceSession mSurfaceSession;
+ int mNumWindow = 0;
+ boolean mClientDead = false;
+
+ /**
+ * Current pointer move event being dispatched to client window... must
+ * hold key lock to access.
+ */
+ QueuedEvent mPendingPointerMove;
+ WindowState mPendingPointerWindow;
+
+ /**
+ * Current trackball move event being dispatched to client window... must
+ * hold key lock to access.
+ */
+ QueuedEvent mPendingTrackballMove;
+ WindowState mPendingTrackballWindow;
+
+ public Session(IBinder token) {
+ mToken = token;
+ mUid = Binder.getCallingUid();
+ mPid = Binder.getCallingPid();
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ // The caller has died, so we can just forget about this.
+ }
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ // Log all 'real' exceptions thrown to the caller
+ if (!(e instanceof SecurityException)) {
+ Log.e(TAG, "Window Session Crash", e);
+ }
+ throw e;
+ }
+ }
+
+ public void binderDied() {
+ synchronized(mWindowMap) {
+ mClientDead = true;
+ killSessionLocked();
+ }
+ }
+
+ public int add(IWindow window, WindowManager.LayoutParams attrs,
+ int viewVisibility, Rect outCoveredInsets) {
+ return addWindow(this, window, attrs, viewVisibility, outCoveredInsets);
+ }
+
+ public void remove(IWindow window) {
+ removeWindow(this, window);
+ }
+
+ public int relayout(IWindow window, WindowManager.LayoutParams attrs,
+ int requestedWidth, int requestedHeight, int viewFlags,
+ Rect outFrame, Rect outCoveredInsets, Surface outSurface) {
+ return relayoutWindow(this, window, attrs,
+ requestedWidth, requestedHeight, viewFlags,
+ outFrame, outCoveredInsets, outSurface);
+ }
+
+ public void finishDrawing(IWindow window) {
+ if (localLOGV) Log.v(
+ TAG, "IWindow finishDrawing called for " + window);
+ finishDrawingWindow(this, window);
+ }
+
+ public void finishKey(IWindow window) {
+ if (localLOGV) Log.v(
+ TAG, "IWindow finishKey called for " + window);
+ mKeyWaiter.finishedKey(this, window, false,
+ KeyWaiter.RETURN_NOTHING);
+ }
+
+ public MotionEvent getPendingPointerMove(IWindow window) {
+ if (localLOGV) Log.v(
+ TAG, "IWindow getPendingMotionEvent called for " + window);
+ return mKeyWaiter.finishedKey(this, window, false,
+ KeyWaiter.RETURN_PENDING_POINTER);
+ }
+
+ public MotionEvent getPendingTrackballMove(IWindow window) {
+ if (localLOGV) Log.v(
+ TAG, "IWindow getPendingMotionEvent called for " + window);
+ return mKeyWaiter.finishedKey(this, window, false,
+ KeyWaiter.RETURN_PENDING_TRACKBALL);
+ }
+
+ public void setTransparentRegion(IWindow window, Region region) {
+ setTransparentRegionWindow(this, window, region);
+ }
+
+ public void setInTouchMode(boolean mode) {
+ synchronized(mWindowMap) {
+ mInTouchMode = mode;
+ }
+ }
+
+ public boolean getInTouchMode() {
+ synchronized(mWindowMap) {
+ return mInTouchMode;
+ }
+ }
+
+ void windowAddedLocked() {
+ if (mSurfaceSession == null) {
+ if (localLOGV) Log.v(
+ TAG, "First window added to " + this + ", creating SurfaceSession");
+ mSurfaceSession = new SurfaceSession();
+ mSessions.add(this);
+ }
+ mNumWindow++;
+ }
+
+ void windowRemovedLocked() {
+ mNumWindow--;
+ killSessionLocked();
+ }
+
+ void killSessionLocked() {
+ if (mNumWindow <= 0 && mClientDead) {
+ mSessions.remove(this);
+ if (mSurfaceSession != null) {
+ if (localLOGV) Log.v(
+ TAG, "Last window removed from " + this
+ + ", destroying " + mSurfaceSession);
+ try {
+ mSurfaceSession.kill();
+ } catch (Exception e) {
+ Log.w(TAG, "Exception thrown when killing surface session "
+ + mSurfaceSession + " in session " + this
+ + ": " + e.toString());
+ }
+ mSurfaceSession = null;
+ }
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+ pw.println(prefix + "mNumWindow=" + mNumWindow
+ + " mClientDead=" + mClientDead
+ + " mSurfaceSession=" + mSurfaceSession);
+ pw.println(prefix + "mPendingPointerWindow=" + mPendingPointerWindow
+ + " mPendingPointerMove=" + mPendingPointerMove);
+ pw.println(prefix + "mPendingTrackballWindow=" + mPendingTrackballWindow
+ + " mPendingTrackballMove=" + mPendingTrackballMove);
+ }
+
+ @Override
+ public String toString() {
+ return "Session{"
+ + Integer.toHexString(System.identityHashCode(this)) + "}";
+ }
+ }
+
+ // -------------------------------------------------------------
+ // Client Window State
+ // -------------------------------------------------------------
+
+ private final class WindowState implements WindowManagerPolicy.WindowState {
+ final Session mSession;
+ final IWindow mClient;
+ WindowToken mToken;
+ AppWindowToken mAppToken;
+ final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
+ final DeathRecipient mDeathRecipient;
+ final WindowState mAttachedWindow;
+ final ArrayList mChildWindows = new ArrayList();
+ final int mBaseLayer;
+ final int mSubLayer;
+ int mViewVisibility;
+ boolean mPolicyVisibility = true;
+ boolean mAppFreezing;
+ Surface mSurface;
+ boolean mAttachedHidden; // is our parent window hidden?
+ boolean mLastHidden; // was this window last hidden?
+ int mRequestedWidth;
+ int mRequestedHeight;
+ int mLastRequestedWidth;
+ int mLastRequestedHeight;
+ int mReqXPos;
+ int mReqYPos;
+ int mLayer;
+ int mAnimLayer;
+ int mLastLayer;
+ boolean mHaveFrame;
+
+ // Actual frame shown on-screen (may be modified by animation)
+ final Rect mShownFrame = new Rect();
+ final Rect mLastShownFrame = new Rect();
+
+ /**
+ * Insets that are covered by system windows
+ */
+ final Rect mCoveredInsets = new Rect();
+
+ // Current transformation being applied.
+ float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
+ float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
+ float mHScale=1, mVScale=1;
+ float mLastHScale=1, mLastVScale=1;
+ final Matrix mTmpMatrix = new Matrix();
+
+ // "Real" frame that the application sees.
+ final Rect mFrame = new Rect();
+ final Rect mLastFrame = new Rect();
+
+ final Rect mContainingFrame = new Rect();
+
+ float mShownAlpha = 1;
+ float mAlpha = 1;
+ float mLastAlpha = 1;
+
+ // Set to true if, when the window gets displayed, it should perform
+ // an enter animation.
+ boolean mEnterAnimationPending;
+
+ // Currently running animation.
+ boolean mAnimating;
+ Animation mAnimation;
+ boolean mAnimationIsEntrance;
+ boolean mHasTransformation;
+ final Transformation mTransformation = new Transformation();
+
+ // This is set after IWindowSession.relayout() has been called at
+ // least once for the window. It allows us to detect the situation
+ // where we don't yet have a surface, but should have one soon, so
+ // we can give the window focus before waiting for the relayout.
+ boolean mRelayoutCalled;
+
+ // This is set after the Surface has been created but before the
+ // window has been drawn. During this time the surface is hidden.
+ boolean mDrawPending;
+
+ // This is set after the window has finished drawing for the first
+ // time but before its surface is shown. The surface will be
+ // displayed when the next layout is run.
+ boolean mCommitDrawPending;
+
+ // This is set during the time after the window's drawing has been
+ // committed, and before its surface is actually shown. It is used
+ // to delay showing the surface until all windows in a token are ready
+ // to be shown.
+ boolean mReadyToShow;
+
+ // Set when the window has been shown in the screen the first time.
+ boolean mHasDrawn;
+
+ // Currently running an exit animation?
+ boolean mExiting;
+
+ // Currently on the mDestroySurface list?
+ boolean mDestroying;
+
+ // Completely remove from window manager after exit animation?
+ boolean mRemoveOnExit;
+
+ // Set when the orientation is changing and this window has not yet
+ // been updated for the new orientation.
+ boolean mOrientationChanging;
+
+ WindowState(Session s, IWindow c, WindowToken token,
+ WindowState attachedWindow, WindowManager.LayoutParams a,
+ int viewVisibility) {
+ mSession = s;
+ mClient = c;
+ mToken = token;
+ mAttrs.copyFrom(a);
+ mViewVisibility = viewVisibility;
+ DeathRecipient deathRecipient = new DeathRecipient();
+ mAlpha = a.alpha;
+ if (localLOGV) Log.v(
+ TAG, "Window " + this + " client=" + c.asBinder()
+ + " token=" + token + " (" + mAttrs.token + ")");
+ try {
+ c.asBinder().linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient = null;
+ mAttachedWindow = null;
+ mBaseLayer = 0;
+ mSubLayer = 0;
+ return;
+ }
+ mDeathRecipient = deathRecipient;
+
+ if (mAttrs.type >= mAttrs.FIRST_SUB_WINDOW &&
+ mAttrs.type <= mAttrs.LAST_SUB_WINDOW) {
+ // The multiplier here is to reserve space for multiple
+ // windows in the same type layer.
+ mBaseLayer = mPolicy.windowTypeToLayerLw(
+ attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
+ mAttachedWindow = attachedWindow;
+ mAttachedWindow.mChildWindows.add(this);
+ } else {
+ // The multiplier here is to reserve space for multiple
+ // windows in the same type layer.
+ mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ mSubLayer = 0;
+ mAttachedWindow = null;
+ }
+
+ WindowState appWin = this;
+ while (appWin.mAttachedWindow != null) {
+ appWin = mAttachedWindow;
+ }
+ WindowToken appToken = appWin.mToken;
+ while (appToken.appWindowToken == null) {
+ WindowToken parent = mTokenMap.get(appToken.token);
+ if (parent == null || appToken == parent) {
+ break;
+ }
+ appToken = parent;
+ }
+ mAppToken = appToken.appWindowToken;
+
+ mSurface = null;
+ mRequestedWidth = 0;
+ mRequestedHeight = 0;
+ mLastRequestedWidth = 0;
+ mLastRequestedHeight = 0;
+ mReqXPos = 0;
+ mReqYPos = 0;
+ mLayer = 0;
+ mAnimLayer = 0;
+ mLastLayer = 0;
+ }
+
+ void attach() {
+ if (localLOGV) Log.v(
+ TAG, "Attaching " + this + " token=" + mToken
+ + ", list=" + mToken.windows);
+ mSession.windowAddedLocked();
+ }
+
+ public void computeFrameLw(int pl, int pt, int pr, int pb,
+ int dl, int dt, int dr, int db) {
+ mHaveFrame = true;
+
+ final int pw = pr-pl;
+ final int ph = pb-pt;
+
+ int w,h;
+ if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {
+ w = mAttrs.width < 0 ? pw : mAttrs.width;
+ h = mAttrs.height< 0 ? ph : mAttrs.height;
+ } else {
+ w = mAttrs.width == mAttrs.FILL_PARENT ? pw : mRequestedWidth;
+ h = mAttrs.height== mAttrs.FILL_PARENT ? ph : mRequestedHeight;
+ }
+
+ final Rect container = mContainingFrame;
+ container.left = pl;
+ container.top = pt;
+ container.right = pr;
+ container.bottom = pb;
+
+ final Rect frame = mFrame;
+
+ //System.out.println("In: w=" + w + " h=" + h + " container=" +
+ // container + " x=" + mAttrs.x + " y=" + mAttrs.y);
+
+ Gravity.apply(mAttrs.gravity, w, h, container,
+ (int) (mAttrs.x + mAttrs.horizontalMargin * pw),
+ (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);
+
+ //System.out.println("Out: " + mFrame);
+
+ // Now make sure the window fits in the overall display.
+ int off = 0;
+ if (frame.left < dl) off = dl-frame.left;
+ else if (frame.right > dr) off = dr-frame.right;
+ if (off != 0) {
+ if (frame.width() > (dr-dl)) {
+ frame.left = dl;
+ frame.right = dr;
+ } else {
+ frame.left += off;
+ frame.right += off;
+ }
+ }
+ off = 0;
+ if (frame.top < dt) off = dt-frame.top;
+ else if (frame.bottom > db) off = db-frame.bottom;
+ if (off != 0) {
+ if (frame.height() > (db-dt)) {
+ frame.top = dt;
+ frame.bottom = db;
+ } else {
+ frame.top += off;
+ frame.bottom += off;
+ }
+ }
+
+ if (localLOGV) Log.v(TAG, "Resolving (mRequestedWidth="
+ + mRequestedWidth + ", mRequestedheight="
+ + mRequestedHeight + ") to" + " (pw=" + pw + ", dh=" + ph
+ + "): frame=" + mFrame);
+ }
+
+ public void setFrameLw(int left, int top, int right, int bottom)
+ {
+ mHaveFrame = true;
+ mFrame.left = left;
+ mFrame.top = top;
+ mFrame.right = right;
+ mFrame.bottom = bottom;
+ }
+
+ public Rect getFrameLw()
+ {
+ return mFrame;
+ }
+
+ public WindowManager.LayoutParams getAttrs()
+ {
+ return mAttrs;
+ }
+
+ public IApplicationToken getAppToken()
+ {
+ return mAppToken != null ? mAppToken.appToken : null;
+ }
+
+ public boolean hasAppShownWindows()
+ {
+ return mAppToken != null ? mAppToken.firstWindowDrawn : false;
+ }
+
+ public boolean hasAppStartingIcon() {
+ return mAppToken != null ? (mAppToken.startingData != null) : false;
+ }
+
+ public WindowManagerPolicy.WindowState getAppStartingWindow() {
+ return mAppToken != null ? mAppToken.startingWindow : null;
+ }
+
+ public void setAnimation(Animation anim) {
+ if (localLOGV) Log.v(
+ TAG, "Setting animation in " + this + ": " + anim);
+ mAnimating = false;
+ mAnimation = anim;
+ mAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mAnimation.scaleCurrentDuration(mWindowAnimationScale);
+ }
+
+ public void clearAnimation() {
+ if (mAnimation != null) {
+ mAnimating = true;
+ mAnimation = null;
+ }
+ }
+
+ Surface createSurfaceLocked() {
+ if (mSurface == null) {
+ mDrawPending = true;
+ mCommitDrawPending = false;
+ mReadyToShow = false;
+ if (mAppToken != null) {
+ mAppToken.allDrawn = false;
+ }
+
+ int flags = 0;
+ if (mAttrs.memoryType == MEMORY_TYPE_HARDWARE) {
+ flags |= Surface.HARDWARE;
+ } else if (mAttrs.memoryType == MEMORY_TYPE_GPU) {
+ flags |= Surface.GPU;
+ } else if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
+ flags |= Surface.PUSH_BUFFERS;
+ }
+
+ if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+ flags |= Surface.SECURE;
+ }
+ if (DEBUG_VISIBILITY) Log.v(
+ TAG, "Creating surface in session "
+ + mSession.mSurfaceSession + " window " + this
+ + " w=" + mFrame.width()
+ + " h=" + mFrame.height() + " format="
+ + mAttrs.format + " flags=" + flags);
+
+ int w = mFrame.width();
+ int h = mFrame.height();
+ if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+ // for a scaled surface, we always want the requested
+ // size.
+ w = mRequestedWidth;
+ h = mRequestedHeight;
+ }
+
+ try {
+ mSurface = new Surface(
+ mSession.mSurfaceSession, mSession.mPid,
+ 0, w, h, mAttrs.format, flags);
+ } catch (Surface.OutOfResourcesException e) {
+ Log.w(TAG, "OutOfResourcesException creating surface");
+ reclaimSomeSurfaceMemoryLocked(this, "create");
+ return null;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating surface", e);
+ return null;
+ }
+
+ if (localLOGV) Log.v(
+ TAG, "Got surface: " + mSurface
+ + ", set left=" + mFrame.left + " top=" + mFrame.top
+ + ", animLayer=" + mAnimLayer);
+ if (SHOW_TRANSACTIONS) {
+ Log.i(TAG, ">>> OPEN TRANSACTION");
+ Log.i(TAG, " SURFACE " + mSurface + ": CREATE ("
+ + mAttrs.getTitle() + ") pos=(" +
+ mFrame.left + "," + mFrame.top + ") (" +
+ mFrame.width() + "x" + mFrame.height() + "), layer=" +
+ mAnimLayer + " HIDE");
+ }
+ Surface.openTransaction();
+ try {
+ try {
+ mSurface.setPosition(mFrame.left, mFrame.top);
+ mSurface.setLayer(mAnimLayer);
+ mSurface.hide();
+ if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
+ mSurface.setFlags(Surface.SURFACE_DITHER,
+ Surface.SURFACE_DITHER);
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Error creating surface in " + w, e);
+ reclaimSomeSurfaceMemoryLocked(this, "create-init");
+ }
+ mLastHidden = true;
+ } finally {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION");
+ Surface.closeTransaction();
+ }
+ if (localLOGV) Log.v(
+ TAG, "Created surface " + this);
+ }
+ return mSurface;
+ }
+
+ void destroySurfaceLocked() {
+ // Window is no longer on-screen, so can no longer receive
+ // key events... if we were waiting for it to finish
+ // handling a key event, the wait is over!
+ mKeyWaiter.finishedKey(mSession, mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ mKeyWaiter.releasePendingPointerLocked(mSession);
+ mKeyWaiter.releasePendingTrackballLocked(mSession);
+
+ if (mAppToken != null && this == mAppToken.startingWindow) {
+ mAppToken.startingDisplayed = false;
+ }
+
+ if (localLOGV) Log.v(
+ TAG, "Window " + this
+ + " destroying surface " + mSurface + ", session " + mSession);
+ if (mSurface != null) {
+ try {
+ if (SHOW_TRANSACTIONS) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Log.i(TAG, " SURFACE " + mSurface + ": DESTROY ("
+ + mAttrs.getTitle() + ")", ex);
+ }
+ mSurface.clear();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception thrown when destroying Window " + this
+ + " surface " + mSurface + " session " + mSession
+ + ": " + e.toString());
+ }
+ mSurface = null;
+ mDrawPending = false;
+ mCommitDrawPending = false;
+ mReadyToShow = false;
+
+ int i = mChildWindows.size();
+ while (i > 0) {
+ i--;
+ WindowState c = (WindowState)mChildWindows.get(i);
+ c.mAttachedHidden = true;
+ }
+ }
+ }
+
+ boolean finishDrawingLocked() {
+ if (mDrawPending) {
+ if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Log.v(
+ TAG, "finishDrawingLocked: " + mSurface);
+ mCommitDrawPending = true;
+ mDrawPending = false;
+ return true;
+ }
+ return false;
+ }
+
+ // This must be called while inside a transaction.
+ void commitFinishDrawingLocked(long currentTime) {
+ //Log.i(TAG, "commitFinishDrawingLocked: " + mSurface);
+ if (!mCommitDrawPending) {
+ return;
+ }
+ mCommitDrawPending = false;
+ mReadyToShow = true;
+ final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING;
+ final AppWindowToken atoken = mAppToken;
+ if (atoken == null || atoken.allDrawn || starting) {
+ performShowLocked();
+ }
+ }
+
+ // This must be called while inside a transaction.
+ boolean performShowLocked() {
+ if (DEBUG_VISIBILITY) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.v(TAG, "performShow on " + this
+ + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
+ + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
+ }
+ if (mReadyToShow && isReadyForDisplay()) {
+ if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Log.i(
+ TAG, " SURFACE " + mSurface + ": SHOW (performShowLocked)");
+ if (DEBUG_VISIBILITY) Log.v(TAG, "Showing " + this
+ + " during animation: policyVis=" + mPolicyVisibility
+ + " attHidden=" + mAttachedHidden
+ + " tok.hiddenRequested="
+ + (mAppToken != null ? mAppToken.hiddenRequested : false)
+ + " tok.idden="
+ + (mAppToken != null ? mAppToken.hidden : false)
+ + " animating=" + mAnimating
+ + " tok animating="
+ + (mAppToken != null ? mAppToken.animating : false));
+ if (!showSurfaceRobustlyLocked(this)) {
+ return false;
+ }
+ mLastAlpha = -1;
+ mHasDrawn = true;
+ mLastHidden = false;
+ mReadyToShow = false;
+ enableScreenIfNeededLocked();
+
+ applyEnterAnimationLocked(this);
+
+ int i = mChildWindows.size();
+ while (i > 0) {
+ i--;
+ WindowState c = (WindowState)mChildWindows.get(i);
+ if (c.mSurface != null && c.mAttachedHidden) {
+ c.mAttachedHidden = false;
+ c.performShowLocked();
+ }
+ }
+
+ if (mAttrs.type != TYPE_APPLICATION_STARTING
+ && mAppToken != null) {
+ mAppToken.firstWindowDrawn = true;
+ if (mAnimation == null && mAppToken.startingData != null) {
+ if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting "
+ + mToken
+ + ": first real window is shown, no animation");
+ mFinishedStarting.add(mAppToken);
+ mH.sendEmptyMessage(H.FINISHED_STARTING);
+ }
+ mAppToken.updateReportedVisibilityLocked();
+ }
+ }
+ return true;
+ }
+
+ // This must be called while inside a transaction. Returns true if
+ // there is more animation to run.
+ boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+ if (!mDisplayFrozen) {
+ // We will run animations as long as the display isn't frozen.
+
+ if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
+ mHasTransformation = true;
+ if (!mAnimating) {
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Starting animation in " + this +
+ " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
+ " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale);
+ mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
+ mAnimation.setStartTime(currentTime);
+ mAnimating = true;
+ }
+ mTransformation.clear();
+ final boolean more = mAnimation.getTransformation(
+ currentTime, mTransformation);
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Stepped animation in " + this +
+ ": more=" + more + ", xform=" + mTransformation);
+ if (mAppToken != null && mAppToken.hasTransformation) {
+ mTransformation.compose(mAppToken.transformation);
+ }
+ if (more) {
+ // we're not done!
+ return true;
+ }
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Finished animation in " + this +
+ " @ " + currentTime);
+ mAnimation = null;
+ //WindowManagerService.this.dump();
+ }
+ if (mAppToken != null && mAppToken.hasTransformation) {
+ mAnimating = true;
+ mHasTransformation = true;
+ mTransformation.set(mAppToken.transformation);
+ return false;
+ } else if (mHasTransformation) {
+ // Little trick to get through the path below to act like
+ // we have finished an animation.
+ mAnimating = true;
+ } else if (isAnimating()) {
+ mAnimating = true;
+ }
+ } else if (mAnimation != null) {
+ // If the display is frozen, and there is a pending animation,
+ // clear it and make sure we run the cleanup code.
+ mAnimating = true;
+ mAnimation = null;
+ }
+
+ if (!mAnimating) {
+ return false;
+ }
+
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Animation done in " + this + ": exiting=" + mExiting
+ + ", reportedVisible="
+ + (mAppToken != null ? mAppToken.reportedVisible : false));
+
+ mAnimating = false;
+ mAnimation = null;
+ mAnimLayer = mLayer;
+ mHasTransformation = false;
+ mTransformation.clear();
+ if (mHasDrawn
+ && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
+ && mAppToken != null
+ && mAppToken.firstWindowDrawn
+ && mAppToken.startingData != null) {
+ if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting "
+ + mToken + ": first real window done animating");
+ mFinishedStarting.add(mAppToken);
+ mH.sendEmptyMessage(H.FINISHED_STARTING);
+ }
+
+ finishExit();
+
+ if (mAppToken != null) {
+ mAppToken.updateReportedVisibilityLocked();
+ }
+
+ return false;
+ }
+
+ void finishExit() {
+ if (DEBUG_ANIM) Log.v(
+ TAG, "finishExit in " + this
+ + ": exiting=" + mExiting
+ + " remove=" + mRemoveOnExit
+ + " windowAnimating=" + isWindowAnimating());
+
+ final int N = mChildWindows.size();
+ for (int i=0; i<N; i++) {
+ ((WindowState)mChildWindows.get(i)).finishExit();
+ }
+
+ if (!mExiting) {
+ return;
+ }
+
+ if (isWindowAnimating()) {
+ return;
+ }
+
+ if (localLOGV) Log.v(
+ TAG, "Exit animation finished in " + this
+ + ": remove=" + mRemoveOnExit);
+ if (mSurface != null) {
+ mDestroySurface.add(this);
+ mDestroying = true;
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + mSurface + ": HIDE (finishExit)");
+ try {
+ mSurface.hide();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Error hiding surface in " + this, e);
+ }
+ mLastHidden = true;
+ mKeyWaiter.releasePendingPointerLocked(mSession);
+ }
+ mExiting = false;
+ if (mRemoveOnExit) {
+ mPendingRemove.add(this);
+ mRemoveOnExit = false;
+ }
+ }
+
+ boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ if (dsdx < .99999f || dsdx > 1.00001f) return false;
+ if (dtdy < .99999f || dtdy > 1.00001f) return false;
+ if (dtdx < -.000001f || dtdx > .000001f) return false;
+ if (dsdy < -.000001f || dsdy > .000001f) return false;
+ return true;
+ }
+
+ void computeShownFrameLocked() {
+ final boolean selfTransformation = mHasTransformation;
+ final boolean attachedTransformation = (mAttachedWindow != null
+ && mAttachedWindow.mHasTransformation);
+ if (selfTransformation || attachedTransformation) {
+ // cache often used attributes locally
+ final Rect frame = mFrame;
+ final float tmpFloats[] = mTmpFloats;
+ final Matrix tmpMatrix = mTmpMatrix;
+
+ // Compute the desired transformation.
+ tmpMatrix.setTranslate(frame.left, frame.top);
+ if (selfTransformation) {
+ tmpMatrix.preConcat(mTransformation.getMatrix());
+ }
+ if (attachedTransformation) {
+ tmpMatrix.preConcat(mAttachedWindow.mTransformation.getMatrix());
+ }
+
+ // "convert" it into SurfaceFlinger's format
+ // (a 2x2 matrix + an offset)
+ // Here we must not transform the position of the surface
+ // since it is already included in the transformation.
+ //Log.i(TAG, "Transform: " + matrix);
+
+ tmpMatrix.getValues(tmpFloats);
+ mDsDx = tmpFloats[Matrix.MSCALE_X];
+ mDtDx = tmpFloats[Matrix.MSKEW_X];
+ mDsDy = tmpFloats[Matrix.MSKEW_Y];
+ mDtDy = tmpFloats[Matrix.MSCALE_Y];
+ int x = (int)tmpFloats[Matrix.MTRANS_X];
+ int y = (int)tmpFloats[Matrix.MTRANS_Y];
+ int w = frame.width();
+ int h = frame.height();
+ mShownFrame.set(x, y, x+w, y+h);
+
+ // Now set the alpha... but because our current hardware
+ // can't do alpha transformation on a non-opaque surface,
+ // turn it off if we are running an animation that is also
+ // transforming since it is more important to have that
+ // animation be smooth.
+ mShownAlpha = mAlpha;
+ if (!PixelFormat.formatHasAlpha(mAttrs.format)
+ || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
+ && x == frame.left && y == frame.top)) {
+ //Log.i(TAG, "Applying alpha transform");
+ if (selfTransformation) {
+ mShownAlpha *= mTransformation.getAlpha();
+ }
+ if (attachedTransformation) {
+ mShownAlpha *= mAttachedWindow.mTransformation.getAlpha();
+ }
+ } else {
+ //Log.i(TAG, "Not applying alpha transform");
+ }
+
+ if (localLOGV) Log.v(
+ TAG, "Continuing animation in " + this +
+ ": " + mShownFrame +
+ ", alpha=" + mTransformation.getAlpha());
+ return;
+ }
+
+ mShownFrame.set(mFrame);
+ mShownAlpha = mAlpha;
+ mDsDx = 1;
+ mDtDx = 0;
+ mDsDy = 0;
+ mDtDy = 1;
+ }
+
+ // Is this window visible? It is not visible if there is no
+ // surface, or we are in the process of running an exit animation
+ // that will remove the surface.
+ boolean isVisible() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && mPolicyVisibility && !mAttachedHidden
+ && (atoken == null || !atoken.hiddenRequested)
+ && !mExiting && !mDestroying;
+ }
+
+ // Same as isVisible(), but we also count it as visible between the
+ // call to IWindowSession.add() and the first relayout().
+ boolean isVisibleOrAdding() {
+ final AppWindowToken atoken = mAppToken;
+ return (mSurface != null
+ || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
+ && mPolicyVisibility && !mAttachedHidden
+ && (atoken == null || !atoken.hiddenRequested)
+ && !mExiting && !mDestroying;
+ }
+
+ // Is this window currently on-screen? It is on-screen either if it
+ // is visible or it is currently running an animation before no longer
+ // being visible.
+ boolean isOnScreen() {
+ final AppWindowToken atoken = mAppToken;
+ if (atoken != null) {
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && ((!mAttachedHidden && !atoken.hiddenRequested)
+ || mAnimating || atoken.animating);
+ } else {
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && (!mAttachedHidden || mAnimating);
+ }
+ }
+
+ // Like isOnScreen(), but we don't return true if the window is part
+ // of a transition that has not yet been started.
+ boolean isReadyForDisplay() {
+ final AppWindowToken atoken = mAppToken;
+ if (atoken != null) {
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && ((!mAttachedHidden && !atoken.hidden)
+ || mAnimating || atoken.animating);
+ } else {
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && (!mAttachedHidden || mAnimating);
+ }
+ }
+
+ // Is the window or its container currently animating?
+ boolean isAnimating() {
+ final WindowState attached = mAttachedWindow;
+ final AppWindowToken atoken = mAppToken;
+ return mAnimation != null
+ || (attached != null && attached.mAnimation != null)
+ || (atoken != null &&
+ (atoken.animation != null
+ || atoken.inPendingTransaction));
+ }
+
+ // Is this window currently animating?
+ boolean isWindowAnimating() {
+ return mAnimation != null;
+ }
+
+ // Like isOnScreen, but returns false if the surface hasn't yet
+ // been drawn.
+ public boolean isDisplayedLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && mPolicyVisibility && !mDestroying
+ && !mDrawPending && !mCommitDrawPending
+ && ((!mAttachedHidden &&
+ (atoken == null || !atoken.hiddenRequested))
+ || mAnimating);
+ }
+
+ public boolean fillsScreenLw(int screenWidth, int screenHeight,
+ boolean shownFrame) {
+ if (mSurface == null) {
+ return false;
+ }
+ final Rect frame = shownFrame ? mShownFrame : mFrame;
+ if (frame.left <= 0 && frame.top <= 0
+ && frame.right >= screenWidth
+ && frame.bottom >= screenHeight) {
+ return true;
+ }
+ return false;
+ }
+
+ boolean isFullscreenOpaque(int screenWidth, int screenHeight) {
+ if (mAttrs.format != PixelFormat.OPAQUE || mSurface == null
+ || mAnimation != null || mDrawPending || mCommitDrawPending) {
+ return false;
+ }
+ if (mFrame.left <= 0 && mFrame.top <= 0 &&
+ mFrame.right >= screenWidth && mFrame.bottom >= screenHeight) {
+ return true;
+ }
+ return false;
+ }
+
+ void removeLocked() {
+ if (mAttachedWindow != null) {
+ mAttachedWindow.mChildWindows.remove(this);
+ }
+ destroySurfaceLocked();
+ mSession.windowRemovedLocked();
+ try {
+ mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ } catch (RuntimeException e) {
+ // Ignore if it has already been removed (usually because
+ // we are doing this as part of processing a death note.)
+ }
+ }
+
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ public void binderDied() {
+ try {
+ synchronized(mWindowMap) {
+ WindowState win = windowForClientLocked(mSession, mClient);
+ Log.i(TAG, "WIN DEATH: " + win);
+ if (win != null) {
+ removeWindowLocked(mSession, win);
+ }
+ }
+ } catch (IllegalArgumentException ex) {
+ // This will happen if the window has already been
+ // removed.
+ }
+ }
+ }
+
+ /** Returns true if this window desires key events. */
+ public final boolean canReceiveKeys() {
+ return isVisibleOrAdding()
+ && (mViewVisibility == View.VISIBLE)
+ && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
+ }
+
+ public boolean hasDrawnLw() {
+ return mHasDrawn;
+ }
+
+ public void showLw() {
+ if (!mPolicyVisibility) {
+ mSurfacesChanged = true;
+ mPolicyVisibility = true;
+ requestAnimationLocked(0);
+ }
+ }
+
+ public void hideLw() {
+ if (mPolicyVisibility) {
+ mSurfacesChanged = true;
+ mPolicyVisibility = false;
+ requestAnimationLocked(0);
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+ pw.println(prefix + "mSession=" + mSession
+ + " mClient=" + mClient.asBinder());
+ pw.println(prefix + "mAttrs=" + mAttrs);
+ pw.println(prefix + "mAttachedWindow=" + mAttachedWindow);
+ pw.println(prefix + "mBaseLayer=" + mBaseLayer
+ + " mSubLayer=" + mSubLayer
+ + " mAnimLayer=" + mLayer + "+"
+ + (mAppToken != null ? mAppToken.animLayerAdjustment : 0)
+ + "=" + mAnimLayer
+ + " mLastLayer=" + mLastLayer);
+ pw.println(prefix + "mSurface=" + mSurface);
+ pw.println(prefix + "mToken=" + mToken);
+ pw.println(prefix + "mAppToken=" + mAppToken);
+ pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
+ + " mPolicyVisibility=" + mPolicyVisibility
+ + " mAttachedHidden=" + mAttachedHidden
+ + " mLastHidden=" + mLastHidden
+ + " mHaveFrame=" + mHaveFrame);
+ pw.println(prefix + "Requested w=" + mRequestedWidth + " h=" + mRequestedHeight
+ + " x=" + mReqXPos + " y=" + mReqYPos);
+ pw.println(prefix + "mShownFrame=" + mShownFrame
+ + " last=" + mLastShownFrame);
+ pw.println(prefix + "mFrame=" + mFrame + " last=" + mLastFrame);
+ pw.println(prefix + "mContainingFrame=" + mContainingFrame
+ + " mCoveredInsets=" + mCoveredInsets);
+ pw.println(prefix + "mShownAlpha=" + mShownAlpha
+ + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha);
+ pw.println(prefix + "mAnimating=" + mAnimating
+ + " mAnimationIsEntrance=" + mAnimationIsEntrance
+ + " mAnimation=" + mAnimation);
+ pw.println(prefix + "mHasTransformation=" + mHasTransformation
+ + " mTransformation=" + mTransformation);
+ pw.println(prefix + "mDrawPending=" + mDrawPending
+ + " mCommitDrawPending=" + mCommitDrawPending
+ + " mReadyToShow=" + mReadyToShow
+ + " mHasDrawn=" + mHasDrawn);
+ pw.println(prefix + "mExiting=" + mExiting
+ + " mRemoveOnExit=" + mRemoveOnExit
+ + " mDestroying=" + mDestroying);
+ pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
+ + " mAppFreezing=" + mAppFreezing);
+ }
+
+ @Override
+ public String toString() {
+ return "Window{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + mAttrs.getTitle() + "}";
+ }
+
+ public void setCoveredInsetsLw(int l, int t, int r, int b) {
+ mCoveredInsets.set(l, t, r, b);
+ }
+ }
+
+ // -------------------------------------------------------------
+ // Window Token State
+ // -------------------------------------------------------------
+
+ class WindowToken {
+ // The actual token.
+ final IBinder token;
+
+ // If this is an AppWindowToken, this is non-null.
+ AppWindowToken appWindowToken;
+
+ // All of the windows associated with this token.
+ final ArrayList<WindowState> windows = new ArrayList<WindowState>();
+
+ // Is key dispatching paused for this token?
+ boolean paused = false;
+
+ WindowToken(IBinder _token) {
+ token = _token;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+ pw.println(prefix + "token=" + token);
+ pw.println(prefix + "windows=" + windows);
+ }
+
+ @Override
+ public String toString() {
+ return "WindowToken{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " token=" + token + "}";
+ }
+ };
+
+ class AppWindowToken extends WindowToken {
+ // Non-null only for application tokens.
+ final IApplicationToken appToken;
+
+ // All of the windows and child windows that are included in this
+ // application token. Note this list is NOT sorted!
+ final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
+
+ int groupId = -1;
+ boolean appFullscreen;
+ int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ // These are used for determining when all windows associated with
+ // an activity have been drawn, so they can be made visible together
+ // at the same time.
+ int lastTransactionSequence = mTransactionSequence-1;
+ int numInterestingWindows;
+ int numDrawnWindows;
+ boolean inPendingTransaction;
+ boolean allDrawn;
+
+ // Is this token going to be hidden in a little while? If so, it
+ // won't be taken into account for setting the screen orientation.
+ boolean willBeHidden;
+
+ // Should this token's windows be hidden?
+ boolean hidden;
+
+ // Is this window's surface needed? This is almost like hidden, except
+ // it will sometimes be true a little earlier: when the token has
+ // been shown, but is still waiting for its app transition to execute
+ // before making its windows shown.
+ boolean hiddenRequested;
+
+ // Have we told the window clients to hide themselves?
+ boolean clientHidden;
+
+ // Temporary for finding which tokens no longer have visible windows.
+ boolean hasVisible;
+
+ // Last visibility state we reported to the app token.
+ boolean reportedVisible;
+
+ // Set to true when the token has been removed from the window mgr.
+ boolean removed;
+
+ // Have we been asked to have this token keep the screen frozen?
+ boolean freezingScreen;
+
+ boolean animating;
+ Animation animation;
+ boolean hasTransformation;
+ final Transformation transformation = new Transformation();
+
+ // Offset to the window of all layers in the token, for use by
+ // AppWindowToken animations.
+ int animLayerAdjustment;
+
+ // Information about an application starting window if displayed.
+ StartingData startingData;
+ WindowState startingWindow;
+ View startingView;
+ boolean startingDisplayed;
+ boolean startingMoved;
+ boolean firstWindowDrawn;
+
+ AppWindowToken(IApplicationToken _token) {
+ super(_token.asBinder());
+ appWindowToken = this;
+ appToken = _token;
+ }
+
+ public void setAnimation(Animation anim) {
+ if (localLOGV) Log.v(
+ TAG, "Setting animation in " + this + ": " + anim);
+ animation = anim;
+ animating = false;
+ anim.restrictDuration(MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mTransitionAnimationScale);
+ int zorder = anim.getZAdjustment();
+ int adj = 0;
+ if (zorder == Animation.ZORDER_TOP) {
+ adj = TYPE_LAYER_OFFSET;
+ } else if (zorder == Animation.ZORDER_BOTTOM) {
+ adj = -TYPE_LAYER_OFFSET;
+ }
+
+ if (animLayerAdjustment != adj) {
+ animLayerAdjustment = adj;
+ updateLayers();
+ }
+ }
+
+ public void setDummyAnimation() {
+ if (animation == null) {
+ if (localLOGV) Log.v(
+ TAG, "Setting dummy animation in " + this);
+ animation = sDummyAnimation;
+ }
+ }
+
+ public void clearAnimation() {
+ if (animation != null) {
+ animation = null;
+ animating = true;
+ }
+ }
+
+ void updateLayers() {
+ final int N = allAppWindows.size();
+ for (int i=0; i<N; i++) {
+ WindowState w = allAppWindows.get(i);
+ w.mAnimLayer = w.mLayer + animLayerAdjustment;
+ }
+ }
+
+ void sendAppVisibilityToClients() {
+ final int N = allAppWindows.size();
+ for (int i=0; i<N; i++) {
+ WindowState win = allAppWindows.get(i);
+ if (win == startingWindow && clientHidden) {
+ // Don't hide the starting window.
+ continue;
+ }
+ try {
+ if (DEBUG_VISIBILITY) Log.v(TAG,
+ "Setting visibility of " + win + ": " + (!clientHidden));
+ win.mClient.dispatchAppVisibility(!clientHidden);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void showAllWindowsLocked() {
+ final int NW = allAppWindows.size();
+ for (int i=0; i<NW; i++) {
+ WindowState w = allAppWindows.get(i);
+ if (DEBUG_VISIBILITY) Log.v(TAG,
+ "performing show on: " + w);
+ w.performShowLocked();
+ }
+ }
+
+ // This must be called while inside a transaction.
+ boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+ if (!mDisplayFrozen) {
+ // We will run animations as long as the display isn't frozen.
+
+ if (animation == sDummyAnimation) {
+ // This guy is going to animate, but not yet. For now count
+ // it is not animating for purposes of scheduling transactions;
+ // when it is really time to animate, this will be set to
+ // a real animation and the next call will execute normally.
+ return false;
+ }
+
+ if ((allDrawn || animating || startingDisplayed) && animation != null) {
+ if (!animating) {
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Starting animation in " + this +
+ " @ " + currentTime + ": dw=" + dw + " dh=" + dh
+ + " scale=" + mTransitionAnimationScale
+ + " allDrawn=" + allDrawn + " animating=" + animating);
+ animation.initialize(dw, dh, dw, dh);
+ animation.setStartTime(currentTime);
+ animating = true;
+ }
+ transformation.clear();
+ final boolean more = animation.getTransformation(
+ currentTime, transformation);
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Stepped animation in " + this +
+ ": more=" + more + ", xform=" + transformation);
+ if (more) {
+ // we're done!
+ hasTransformation = true;
+ return true;
+ }
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Finished animation in " + this +
+ " @ " + currentTime);
+ animation = null;
+ }
+ } else if (animation != null) {
+ // If the display is frozen, and there is a pending animation,
+ // clear it and make sure we run the cleanup code.
+ animating = true;
+ animation = null;
+ }
+
+ hasTransformation = false;
+
+ if (!animating) {
+ return false;
+ }
+
+ clearAnimation();
+ animating = false;
+
+ if (DEBUG_ANIM) Log.v(
+ TAG, "Animation done in " + this
+ + ": reportedVisible=" + reportedVisible);
+
+ transformation.clear();
+ if (animLayerAdjustment != 0) {
+ animLayerAdjustment = 0;
+ updateLayers();
+ }
+
+ final int N = windows.size();
+ for (int i=0; i<N; i++) {
+ ((WindowState)windows.get(i)).finishExit();
+ }
+ updateReportedVisibilityLocked();
+
+ return false;
+ }
+
+ void updateReportedVisibilityLocked() {
+ if (appToken == null) {
+ return;
+ }
+
+ int numInteresting = 0;
+ int numVisible = 0;
+ boolean nowGone = true;
+
+ if (DEBUG_VISIBILITY) Log.v(TAG, "Update reported visibility: " + this);
+ final int N = allAppWindows.size();
+ for (int i=0; i<N; i++) {
+ WindowState win = allAppWindows.get(i);
+ if (win == startingWindow || win.mAppFreezing) {
+ continue;
+ }
+ if (DEBUG_VISIBILITY) {
+ Log.v(TAG, "Win " + win + ": isDisplayed="
+ + win.isDisplayedLw()
+ + ", isAnimating=" + win.isAnimating());
+ if (!win.isDisplayedLw()) {
+ Log.v(TAG, "Not displayed: s=" + win.mSurface
+ + " pv=" + win.mPolicyVisibility
+ + " dp=" + win.mDrawPending
+ + " cdp=" + win.mCommitDrawPending
+ + " ah=" + win.mAttachedHidden
+ + " th="
+ + (win.mAppToken != null
+ ? win.mAppToken.hiddenRequested : false)
+ + " a=" + win.mAnimating);
+ }
+ }
+ numInteresting++;
+ if (win.isDisplayedLw()) {
+ if (!win.isAnimating()) {
+ numVisible++;
+ }
+ nowGone = false;
+ } else if (win.isAnimating()) {
+ nowGone = false;
+ }
+ }
+
+ boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
+ if (DEBUG_VISIBILITY) Log.v(TAG, "VIS " + this + ": interesting="
+ + numInteresting + " visible=" + numVisible);
+ if (nowVisible != reportedVisible) {
+ if (DEBUG_VISIBILITY) Log.v(
+ TAG, "Visibility changed in " + this
+ + ": vis=" + nowVisible);
+ reportedVisible = nowVisible;
+ Message m = mH.obtainMessage(
+ H.REPORT_APPLICATION_TOKEN_WINDOWS,
+ nowVisible ? 1 : 0,
+ nowGone ? 1 : 0,
+ this);
+ mH.sendMessage(m);
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ super.dump(pw, prefix);
+ pw.println(prefix + "app=" + (appToken != null));
+ pw.println(prefix + "allAppWindows=" + allAppWindows);
+ pw.println(prefix + "groupId=" + groupId
+ + " requestedOrientation=" + requestedOrientation);
+ pw.println(prefix + "hidden=" + hidden
+ + " hiddenRequested=" + hiddenRequested
+ + " clientHidden=" + clientHidden
+ + " willBeHidden=" + willBeHidden);
+ pw.println(prefix + "paused=" + paused
+ + " freezingScreen=" + freezingScreen);
+ pw.println(prefix + "numInterestingWindows=" + numInterestingWindows
+ + " numDrawnWindows=" + numDrawnWindows
+ + " inPendingTransaction=" + inPendingTransaction
+ + " allDrawn=" + allDrawn);
+ pw.println(prefix + "hasVisible=" + hasVisible
+ + " reportedVisible=" + reportedVisible);
+ pw.println(prefix + "animating=" + animating
+ + " animation=" + animation);
+ pw.println(prefix + "animLayerAdjustment=" + animLayerAdjustment
+ + " transformation=" + transformation);
+ pw.println(prefix + "startingData=" + startingData
+ + " removed=" + removed
+ + " firstWindowDrawn=" + firstWindowDrawn);
+ pw.println(prefix + "startingWindow=" + startingWindow
+ + " startingView=" + startingView
+ + " startingDisplayed=" + startingDisplayed
+ + " startingMoved" + startingMoved);
+ }
+
+ @Override
+ public String toString() {
+ return "AppWindowToken{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " token=" + token + "}";
+ }
+ }
+
+ public static WindowManager.LayoutParams findAnimations(
+ ArrayList<AppWindowToken> order,
+ ArrayList<AppWindowToken> tokenList1,
+ ArrayList<AppWindowToken> tokenList2) {
+ // We need to figure out which animation to use...
+ WindowManager.LayoutParams animParams = null;
+ int animSrc = 0;
+
+ //Log.i(TAG, "Looking for animations...");
+ for (int i=order.size()-1; i>=0; i--) {
+ AppWindowToken wtoken = order.get(i);
+ //Log.i(TAG, "Token " + wtoken + " with " + wtoken.windows.size() + " windows");
+ if (tokenList1.contains(wtoken) || tokenList2.contains(wtoken)) {
+ int j = wtoken.windows.size();
+ while (j > 0) {
+ j--;
+ WindowState win = wtoken.windows.get(j);
+ //Log.i(TAG, "Window " + win + ": type=" + win.mAttrs.type);
+ if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
+ || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+ //Log.i(TAG, "Found base or application window, done!");
+ if (wtoken.appFullscreen) {
+ return win.mAttrs;
+ }
+ if (animSrc < 2) {
+ animParams = win.mAttrs;
+ animSrc = 2;
+ }
+ } else if (animSrc < 1 && win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
+ //Log.i(TAG, "Found normal window, we may use this...");
+ animParams = win.mAttrs;
+ animSrc = 1;
+ }
+ }
+ }
+ }
+
+ return animParams;
+ }
+
+ // -------------------------------------------------------------
+ // DummyAnimation
+ // -------------------------------------------------------------
+
+ // This is an animation that does nothing: it just immediately finishes
+ // itself every time it is called. It is used as a stub animation in cases
+ // where we want to synchronize multiple things that may be animating.
+ static final class DummyAnimation extends Animation {
+ public boolean getTransformation(long currentTime, Transformation outTransformation) {
+ return false;
+ }
+ }
+ static final Animation sDummyAnimation = new DummyAnimation();
+
+ // -------------------------------------------------------------
+ // Async Handler
+ // -------------------------------------------------------------
+
+ static final class StartingData {
+ final String pkg;
+ final int theme;
+ final CharSequence nonLocalizedLabel;
+ final int labelRes;
+ final int icon;
+
+ StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
+ int _labelRes, int _icon) {
+ pkg = _pkg;
+ theme = _theme;
+ nonLocalizedLabel = _nonLocalizedLabel;
+ labelRes = _labelRes;
+ icon = _icon;
+ }
+ }
+
+ private 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 ANIMATE = 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 UPDATE_ORIENTATION = 10;
+ public static final int WINDOW_FREEZE_TIMEOUT = 11;
+ public static final int HOLD_SCREEN_CHANGED = 12;
+ 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;
+
+ private Session mLastReportedHold;
+
+ public H() {
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case REPORT_FOCUS_CHANGE: {
+ WindowState lastFocus;
+ WindowState newFocus;
+
+ synchronized(mWindowMap) {
+ lastFocus = mLastFocus;
+ newFocus = mCurrentFocus;
+ mLastFocus = newFocus;
+ //Log.i(TAG, "Focus moving from " + lastFocus
+ // + " to " + newFocus);
+ if (newFocus != null && lastFocus != null
+ && !newFocus.isDisplayedLw()) {
+ //Log.i(TAG, "Delaying loss of focus...");
+ mLosingFocus.add(lastFocus);
+ lastFocus = null;
+ }
+ }
+
+ if (lastFocus != newFocus) {
+ //System.out.println("Changing focus from " + lastFocus
+ // + " to " + newFocus);
+ if (newFocus != null) {
+ try {
+ //Log.i(TAG, "Gaining focus: " + newFocus);
+ newFocus.mClient.windowFocusChanged(true, mInTouchMode);
+ } catch (RemoteException e) {
+ // Ignore if process has died.
+ }
+ }
+
+ if (lastFocus != null) {
+ try {
+ //Log.i(TAG, "Losing focus: " + lastFocus);
+ lastFocus.mClient.windowFocusChanged(false, mInTouchMode);
+ } catch (RemoteException e) {
+ // Ignore if process has died.
+ }
+ }
+ }
+ } 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++) {
+ try {
+ //Log.i(TAG, "Losing delayed focus: " + losers.get(i));
+ losers.get(i).mClient.windowFocusChanged(false, mInTouchMode);
+ } catch (RemoteException e) {
+ // Ignore if process has died.
+ }
+ }
+ } break;
+
+ case ANIMATE: {
+ synchronized(mWindowMap) {
+ mAnimationPending = 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) Log.v(TAG, "Add starting "
+ + wtoken + ": pkg=" + sd.pkg);
+
+ View view = null;
+ try {
+ view = mPolicy.addStartingWindow(
+ wtoken.token, sd.pkg,
+ sd.theme, sd.nonLocalizedLabel, sd.labelRes,
+ sd.icon);
+ } catch (Exception e) {
+ Log.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) Log.v(TAG,
+ "Aborted starting " + wtoken
+ + ": removed=" + wtoken.removed
+ + " startingData=" + wtoken.startingData);
+ wtoken.startingWindow = null;
+ wtoken.startingData = null;
+ abort = true;
+ }
+ } else {
+ wtoken.startingView = view;
+ }
+ if (DEBUG_STARTING_WINDOW && !abort) Log.v(TAG,
+ "Added starting " + wtoken
+ + ": startingWindow="
+ + wtoken.startingWindow + " startingView="
+ + wtoken.startingView);
+ }
+
+ if (abort) {
+ try {
+ mPolicy.removeStartingWindow(wtoken.token, view);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception when removing starting window", e);
+ }
+ }
+ }
+ } break;
+
+ case REMOVE_STARTING: {
+ final AppWindowToken wtoken = (AppWindowToken)msg.obj;
+ IBinder token = null;
+ View view = null;
+ synchronized (mWindowMap) {
+ if (DEBUG_STARTING_WINDOW) Log.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;
+ }
+ }
+ if (view != null) {
+ try {
+ mPolicy.removeStartingWindow(token, view);
+ } catch (Exception e) {
+ Log.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) Log.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;
+ }
+
+ try {
+ mPolicy.removeStartingWindow(token, view);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception when removing starting window", e);
+ }
+ }
+ } 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) Log.v(
+ TAG, "Reporting visible in " + wtoken
+ + " visible=" + nowVisible
+ + " gone=" + nowGone);
+ if (nowVisible) {
+ wtoken.appToken.windowsVisible();
+ } else {
+ wtoken.appToken.windowsGone();
+ }
+ } catch (RemoteException ex) {
+ }
+ } break;
+
+ case UPDATE_ORIENTATION: {
+ setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false);
+ break;
+ }
+
+ case WINDOW_FREEZE_TIMEOUT: {
+ synchronized (mWindowMap) {
+ Log.w(TAG, "Window freeze timeout expired.");
+ int i = mWindows.size();
+ while (i > 0) {
+ i--;
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.mOrientationChanging) {
+ w.mOrientationChanging = false;
+ Log.w(TAG, "Force clearing orientation change: " + w);
+ }
+ }
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ break;
+ }
+
+ case HOLD_SCREEN_CHANGED: {
+ Session oldHold;
+ Session newHold;
+ synchronized (mWindowMap) {
+ oldHold = mLastReportedHold;
+ newHold = (Session)msg.obj;
+ mLastReportedHold = newHold;
+ }
+
+ if (oldHold != newHold) {
+ try {
+ if (oldHold != null) {
+ mBatteryStats.noteStopWakelock(oldHold.mUid,
+ "window",
+ BatteryStats.WAKE_TYPE_WINDOW);
+ }
+ if (newHold != null) {
+ mBatteryStats.noteStartWakelock(newHold.mUid,
+ "window",
+ BatteryStats.WAKE_TYPE_WINDOW);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ break;
+ }
+
+ case APP_TRANSITION_TIMEOUT: {
+ synchronized (mWindowMap) {
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "*** APP TRANSITION TIMEOUT");
+ mAppTransitionReady = true;
+ mAppTransitionTimeout = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+ break;
+ }
+
+ case PERSIST_ANIMATION_SCALE: {
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ break;
+ }
+
+ case FORCE_GC: {
+ synchronized(mWindowMap) {
+ if (mAnimationPending) {
+ // If we are animating, don't do the gc now but
+ // delay a bit so we don't interrupt the animation.
+ mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
+ 2000);
+ return;
+ }
+ // If we are currently rotating the display, it will
+ // schedule a new message when done.
+ if (mDisplayFrozen) {
+ return;
+ }
+ mFreezeGcPending = 0;
+ }
+ Runtime.getRuntime().gc();
+ break;
+ }
+
+ case ENABLE_SCREEN: {
+ performEnableScreen();
+ break;
+ }
+
+ case APP_FREEZE_TIMEOUT: {
+ synchronized (mWindowMap) {
+ Log.w(TAG, "App freeze timeout expired.");
+ int i = mAppTokens.size();
+ while (i > 0) {
+ i--;
+ AppWindowToken tok = mAppTokens.get(i);
+ if (tok.freezingScreen) {
+ Log.w(TAG, "Force clearing freeze: " + tok);
+ unsetAppFreezingScreenLocked(tok, true, true);
+ }
+ }
+ }
+ break;
+ }
+
+ }
+ }
+ }
+
+ // -------------------------------------------------------------
+ // IWindowManager API
+ // -------------------------------------------------------------
+
+ public IWindowSession openSession(IBinder token) {
+ return new Session(token);
+ }
+
+ // -------------------------------------------------------------
+ // Internals
+ // -------------------------------------------------------------
+
+ final WindowState windowForClientLocked(Session session, IWindow client) {
+ return windowForClientLocked(session, client.asBinder());
+ }
+
+ final WindowState windowForClientLocked(Session session, IBinder client) {
+ WindowState win = mWindowMap.get(client);
+ if (localLOGV) Log.v(
+ TAG, "Looking up client " + client + ": " + win);
+ if (win == null) {
+ RuntimeException ex = new RuntimeException();
+ Log.w(TAG, "Requested window " + client + " does not exist", ex);
+ return null;
+ }
+ if (win.mSession != session) {
+ RuntimeException ex = new RuntimeException();
+ Log.w(TAG, "Requested window " + client + " is in session " +
+ win.mSession + ", not " + session, ex);
+ return null;
+ }
+
+ return win;
+ }
+
+ private final void assignLayersLocked() {
+ if (mInLayout) {
+ if (Config.DEBUG) {
+ throw new RuntimeException("Recursive call!");
+ }
+ return;
+ }
+
+ int N = mWindows.size();
+ int curBaseLayer = 0;
+ int curLayer = 0;
+ int i;
+
+ for (i=0; i<N; i++) {
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.mBaseLayer == curBaseLayer) {
+ curLayer += WINDOW_LAYER_MULTIPLIER;
+ w.mLayer = curLayer;
+ } else {
+ curBaseLayer = curLayer = w.mBaseLayer;
+ w.mLayer = curLayer;
+ }
+ w.mAnimLayer = w.mAppToken != null
+ ? (w.mLayer + w.mAppToken.animLayerAdjustment) : w.mLayer;
+ //System.out.println(
+ // "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
+ }
+ }
+
+ private boolean mInLayout = false;
+ private final void performLayoutAndPlaceSurfacesLocked() {
+ if (mInLayout) {
+ if (Config.DEBUG) {
+ throw new RuntimeException("Recursive call!");
+ }
+ return;
+ }
+
+ boolean recoveringMemory = false;
+ if (mForceRemoves != null) {
+ recoveringMemory = true;
+ // Wait a little it for things to settle down, and off we go.
+ for (int i=0; i<mForceRemoves.size(); i++) {
+ WindowState ws = mForceRemoves.get(i);
+ Log.i(TAG, "Force removing: " + ws);
+ removeWindowInnerLocked(ws.mSession, ws);
+ }
+ mForceRemoves = null;
+ Log.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) {
+ }
+ }
+ }
+
+ mInLayout = true;
+ try {
+ performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
+
+ int i = mPendingRemove.size()-1;
+ if (i >= 0) {
+ while (i >= 0) {
+ WindowState w = mPendingRemove.get(i);
+ removeWindowInnerLocked(w.mSession, w);
+ i--;
+ }
+ mPendingRemove.clear();
+
+ mInLayout = false;
+ assignLayersLocked();
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+
+ } else {
+ mInLayout = false;
+ if (mLayoutNeeded) {
+ requestAnimationLocked(0);
+ }
+ }
+ } catch (RuntimeException e) {
+ mInLayout = false;
+ Log.e(TAG, "Unhandled exception while layout out windows", e);
+ }
+ }
+
+ private final void performLayoutLockedInner() {
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ final int N = mWindows.size();
+ int i;
+
+ // FIRST LOOP: Perform a layout, if needed.
+
+ if (mLayoutNeeded) {
+ mPolicy.beginLayoutLw(dw, dh);
+
+ // First perform layout of any root windows (not attached
+ // to another window).
+ int topAttached = -1;
+ for (i = N-1; i >= 0; i--) {
+ WindowState win = (WindowState) mWindows.get(i);
+
+ boolean gone = win.mViewVisibility == View.GONE
+ || !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 (!gone || !win.mHaveFrame) {
+ if (win.mAttachedWindow == null) {
+ mPolicy.layoutWindowLw(win, win.mAttrs, null);
+ } else {
+ if (topAttached < 0) topAttached = i;
+ }
+ }
+ }
+
+ // 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--) {
+ WindowState win = (WindowState) mWindows.get(i);
+
+ // 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 ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
+ || !win.mHaveFrame) {
+
+ mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
+ }
+ }
+
+ mPolicy.finishLayoutLw();
+ mLayoutNeeded = false;
+ }
+ }
+
+ private final void performLayoutAndPlaceSurfacesLockedInner(
+ boolean recoveringMemory) {
+ final long currentTime = SystemClock.uptimeMillis();
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ final int N = mWindows.size();
+ int i;
+
+ // FIRST LOOP: Perform a layout, if needed.
+
+ performLayoutLockedInner();
+
+ if (mFxSession == null) {
+ mFxSession = new SurfaceSession();
+ }
+
+ if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION");
+
+ // Initialize state of exiting applications.
+ for (i=0; i<mExitingAppTokens.size(); i++) {
+ mExitingAppTokens.get(i).hasVisible = false;
+ }
+
+ // SECOND LOOP: Execute animations and update visibility of windows.
+
+ boolean orientationChangeComplete = true;
+ Session holdScreen = null;
+ boolean focusDisplayed = false;
+ boolean animating = false;
+
+ Surface.openTransaction();
+ try {
+ boolean restart;
+
+ do {
+ final int transactionSequence = ++mTransactionSequence;
+
+ // Update animations of all applications.
+ boolean tokensAnimating = false;
+ final int NAT = mAppTokens.size();
+ for (i=0; i<NAT; i++) {
+ if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+ tokensAnimating = true;
+ }
+ }
+
+ animating = tokensAnimating;
+ restart = false;
+
+ boolean tokenMayBeDrawn = false;
+
+ mPolicy.beginAnimationLw(dw, dh);
+
+ for (i=N-1; i>=0; i--) {
+ WindowState w = (WindowState)mWindows.get(i);
+
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+
+ if (w.mSurface != null) {
+ // Execute animation.
+ w.commitFinishDrawingLocked(currentTime);
+ if (w.stepAnimationLocked(currentTime, dw, dh)) {
+ animating = true;
+ //w.dump(" ");
+ }
+
+ mPolicy.animatingWindowLw(w, attrs);
+ }
+
+ final AppWindowToken atoken = w.mAppToken;
+ if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+ if (atoken.lastTransactionSequence != transactionSequence) {
+ atoken.lastTransactionSequence = transactionSequence;
+ atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+ atoken.startingDisplayed = false;
+ }
+ if ((w.isOnScreen() || w.mAttrs.type
+ == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+ && !w.mExiting && !w.mDestroying) {
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
+ Log.v(TAG, "Eval win " + w + ": isDisplayed="
+ + w.isDisplayedLw()
+ + ", isAnimating=" + w.isAnimating());
+ if (!w.isDisplayedLw()) {
+ Log.v(TAG, "Not displayed: s=" + w.mSurface
+ + " pv=" + w.mPolicyVisibility
+ + " dp=" + w.mDrawPending
+ + " cdp=" + w.mCommitDrawPending
+ + " ah=" + w.mAttachedHidden
+ + " th=" + atoken.hiddenRequested
+ + " a=" + w.mAnimating);
+ }
+ }
+ if (w != atoken.startingWindow) {
+ if (!atoken.freezingScreen || !w.mAppFreezing) {
+ atoken.numInterestingWindows++;
+ if (w.isDisplayedLw()) {
+ atoken.numDrawnWindows++;
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Log.v(TAG,
+ "tokenMayBeDrawn: " + atoken
+ + " freezingScreen=" + atoken.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ tokenMayBeDrawn = true;
+ }
+ }
+ } else if (w.isDisplayedLw()) {
+ atoken.startingDisplayed = true;
+ }
+ }
+ } else if (w.mReadyToShow) {
+ w.performShowLocked();
+ }
+ }
+
+ if (mPolicy.finishAnimationLw()) {
+ restart = true;
+ }
+
+ if (tokenMayBeDrawn) {
+ // See if any windows have been drawn, so they (and others
+ // associated with them) can now be shown.
+ final int NT = mTokenList.size();
+ for (i=0; i<NT; i++) {
+ AppWindowToken wtoken = mTokenList.get(i).appWindowToken;
+ if (wtoken == null) {
+ continue;
+ }
+ if (wtoken.freezingScreen) {
+ int numInteresting = wtoken.numInterestingWindows;
+ if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+ if (DEBUG_VISIBILITY) Log.v(TAG,
+ "allDrawn: " + wtoken
+ + " interesting=" + numInteresting
+ + " drawn=" + wtoken.numDrawnWindows);
+ wtoken.showAllWindowsLocked();
+ unsetAppFreezingScreenLocked(wtoken, false, true);
+ orientationChangeComplete = true;
+ }
+ } else if (!wtoken.allDrawn) {
+ int numInteresting = wtoken.numInterestingWindows;
+ if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+ if (DEBUG_VISIBILITY) Log.v(TAG,
+ "allDrawn: " + wtoken
+ + " interesting=" + numInteresting
+ + " drawn=" + wtoken.numDrawnWindows);
+ wtoken.allDrawn = true;
+ restart = true;
+
+ // We can now show all of the drawn windows!
+ if (!mOpeningApps.contains(wtoken)) {
+ wtoken.showAllWindowsLocked();
+ }
+ }
+ }
+ }
+ }
+
+ // 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 (mAppTransitionReady) {
+ int NN = mOpeningApps.size();
+ boolean goodToGo = true;
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "Checking " + NN + " opening apps (frozen="
+ + mDisplayFrozen + " timeout="
+ + mAppTransitionTimeout + ")...");
+ if (!mDisplayFrozen && !mAppTransitionTimeout) {
+ // 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) Log.v(TAG,
+ "Check opening app" + wtoken + ": allDrawn="
+ + wtoken.allDrawn + " startingDisplayed="
+ + wtoken.startingDisplayed);
+ if (!wtoken.allDrawn && !wtoken.startingDisplayed
+ && !wtoken.startingMoved) {
+ goodToGo = false;
+ }
+ }
+ }
+ if (goodToGo) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "**** GOOD TO GO");
+ int transit = mNextAppTransition;
+ if (mSkipAppTransitionAnimation) {
+ transit = WindowManagerPolicy.TRANSIT_NONE;
+ }
+ mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+ mAppTransitionReady = false;
+ mAppTransitionTimeout = false;
+ mStartingIconInTransition = false;
+ mSkipAppTransitionAnimation = false;
+
+ mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+
+ // We need to figure out which animation to use...
+ WindowManager.LayoutParams lp = findAnimations(mAppTokens,
+ mOpeningApps, mClosingApps);
+
+ NN = mOpeningApps.size();
+ for (i=0; i<NN; i++) {
+ AppWindowToken wtoken = mOpeningApps.get(i);
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "Now opening app" + wtoken);
+ wtoken.reportedVisible = false;
+ wtoken.inPendingTransaction = false;
+ setTokenVisibilityLocked(wtoken, lp, true, transit, false);
+ wtoken.updateReportedVisibilityLocked();
+ wtoken.showAllWindowsLocked();
+ }
+ NN = mClosingApps.size();
+ for (i=0; i<NN; i++) {
+ AppWindowToken wtoken = mClosingApps.get(i);
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "Now closing app" + wtoken);
+ wtoken.inPendingTransaction = false;
+ setTokenVisibilityLocked(wtoken, lp, false, transit, false);
+ wtoken.updateReportedVisibilityLocked();
+ // Force the allDrawn flag, because we want to start
+ // this guy's animations regardless of whether it's
+ // gotten drawn.
+ wtoken.allDrawn = true;
+ }
+
+ mOpeningApps.clear();
+ mClosingApps.clear();
+
+ // This has changed the visibility of windows, so perform
+ // a new layout to get them all up-to-date.
+ mLayoutNeeded = true;
+ performLayoutLockedInner();
+ updateFocusedWindowLocked();
+
+ restart = true;
+ }
+ }
+ } while (restart);
+
+ // THIRD LOOP: Update the surfaces of all windows.
+
+ final boolean someoneLosingFocus = mLosingFocus.size() != 0;
+
+ mSurfacesChanged = false;
+ boolean obscured = false;
+ boolean blurring = false;
+ boolean dimming = false;
+ boolean covered = false;
+ int tint = 0;
+
+ for (i=N-1; i>=0; i--) {
+ WindowState w = (WindowState)mWindows.get(i);
+
+ boolean displayed = false;
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+ final int attrFlags = attrs.flags;
+
+ if (w.mSurface != null) {
+ w.computeShownFrameLocked();
+ if (localLOGV) Log.v(
+ TAG, "Placing surface #" + i + " " + w.mSurface
+ + ": new=" + w.mShownFrame + ", old="
+ + w.mLastShownFrame);
+
+ boolean resize;
+ int width, height;
+ if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
+ resize = w.mLastRequestedWidth != w.mRequestedWidth ||
+ w.mLastRequestedHeight != w.mRequestedHeight;
+ // for a scaled surface, we just want to use
+ // the requested size.
+ width = w.mRequestedWidth;
+ height = w.mRequestedHeight;
+ w.mLastRequestedWidth = width;
+ w.mLastRequestedHeight = height;
+ w.mLastShownFrame.set(w.mShownFrame);
+ try {
+ w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Error positioning surface in " + w, e);
+ if (!recoveringMemory) {
+ reclaimSomeSurfaceMemoryLocked(w, "position");
+ }
+ }
+ } else {
+ resize = !w.mLastShownFrame.equals(w.mShownFrame);
+ width = w.mShownFrame.width();
+ height = w.mShownFrame.height();
+ w.mLastShownFrame.set(w.mShownFrame);
+ if (resize) {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": ("
+ + w.mShownFrame.left + ","
+ + w.mShownFrame.top + ") ("
+ + w.mShownFrame.width() + "x"
+ + w.mShownFrame.height() + ")");
+ }
+ }
+
+ if (resize) {
+ if (width < 1) width = 1;
+ if (height < 1) height = 1;
+ if (w.mSurface != null) {
+ try {
+ w.mSurface.setSize(width, height);
+ w.mSurface.setPosition(w.mShownFrame.left,
+ w.mShownFrame.top);
+ } catch (RuntimeException e) {
+ // If something goes wrong with the surface (such
+ // as running out of memory), don't take down the
+ // entire system.
+ Log.e(TAG, "Failure updating surface of " + w
+ + "size=(" + width + "x" + height
+ + "), pos=(" + w.mShownFrame.left
+ + "," + w.mShownFrame.top + ")", e);
+ if (!recoveringMemory) {
+ reclaimSomeSurfaceMemoryLocked(w, "size");
+ }
+ }
+ }
+ }
+ if (!w.mAppFreezing) {
+ if (!w.mLastFrame.equals(w.mFrame)) {
+ w.mLastFrame.set(w.mFrame);
+ // 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_ORIENTATION) Log.v(TAG,
+ "Orientation start waiting for draw in "
+ + w + ", surface " + w.mSurface);
+ w.mDrawPending = true;
+ w.mCommitDrawPending = false;
+ w.mReadyToShow = false;
+ if (w.mAppToken != null) {
+ w.mAppToken.allDrawn = false;
+ }
+ }
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Resizing window " + w + " to " + w.mFrame);
+ mResizingWindows.add(w);
+ } else if (w.mOrientationChanging) {
+ if (!w.mDrawPending && !w.mCommitDrawPending) {
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Orientation not waiting for draw in "
+ + w + ", surface " + w.mSurface);
+ w.mOrientationChanging = false;
+ }
+ }
+ }
+
+ if (w.mAttachedHidden) {
+ if (!w.mLastHidden) {
+ //dump();
+ w.mLastHidden = true;
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": HIDE (performLayout-attached)");
+ if (w.mSurface != null) {
+ try {
+ w.mSurface.hide();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception hiding surface in " + w);
+ }
+ }
+ mKeyWaiter.releasePendingPointerLocked(w.mSession);
+ }
+ // If we are waiting for this window to handle an
+ // orientation change, well, it is hidden, so
+ // doesn't really matter. Note that this does
+ // introduce a potential glitch if the window
+ // becomes unhidden before it has drawn for the
+ // new orientation.
+ if (w.mOrientationChanging) {
+ w.mOrientationChanging = false;
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Orientation change skips hidden " + w);
+ }
+ } else if (!w.isReadyForDisplay()) {
+ if (!w.mLastHidden) {
+ //dump();
+ w.mLastHidden = true;
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": HIDE (performLayout-ready)");
+ if (w.mSurface != null) {
+ try {
+ w.mSurface.hide();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception exception hiding surface in " + w);
+ }
+ }
+ mKeyWaiter.releasePendingPointerLocked(w.mSession);
+ }
+ // If we are waiting for this window to handle an
+ // orientation change, well, it is hidden, so
+ // doesn't really matter. Note that this does
+ // introduce a potential glitch if the window
+ // becomes unhidden before it has drawn for the
+ // new orientation.
+ if (w.mOrientationChanging) {
+ w.mOrientationChanging = false;
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Orientation change skips hidden " + w);
+ }
+ } else if (w.mLastLayer != w.mAnimLayer
+ || w.mLastAlpha != w.mShownAlpha
+ || w.mLastDsDx != w.mDsDx
+ || w.mLastDtDx != w.mDtDx
+ || w.mLastDsDy != w.mDsDy
+ || w.mLastDtDy != w.mDtDy
+ || w.mLastHScale != w.mHScale
+ || w.mLastVScale != w.mVScale
+ || w.mLastHidden) {
+ displayed = true;
+ w.mLastAlpha = w.mShownAlpha;
+ w.mLastLayer = w.mAnimLayer;
+ w.mLastDsDx = w.mDsDx;
+ w.mLastDtDx = w.mDtDx;
+ w.mLastDsDy = w.mDsDy;
+ w.mLastDtDy = w.mDtDy;
+ w.mLastHScale = w.mHScale;
+ w.mLastVScale = w.mVScale;
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": alpha="
+ + w.mShownAlpha + " layer=" + w.mAnimLayer);
+ if (w.mSurface != null) {
+ try {
+ w.mSurface.setAlpha(w.mShownAlpha);
+ w.mSurface.setLayer(w.mAnimLayer);
+ w.mSurface.setMatrix(
+ w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
+ w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Error updating surface in " + w, e);
+ if (!recoveringMemory) {
+ reclaimSomeSurfaceMemoryLocked(w, "update");
+ }
+ }
+ }
+
+ if (w.mLastHidden && !w.mDrawPending
+ && !w.mCommitDrawPending
+ && !w.mReadyToShow) {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": SHOW (performLayout)");
+ if (DEBUG_VISIBILITY) Log.v(TAG, "Showing " + w
+ + " during relayout");
+ if (showSurfaceRobustlyLocked(w)) {
+ w.mHasDrawn = true;
+ w.mLastHidden = false;
+ } else {
+ w.mOrientationChanging = false;
+ }
+ }
+ if (w.mAppToken != null && w.mSurface != null) {
+ w.mAppToken.hasVisible = true;
+ }
+ } else {
+ displayed = true;
+ }
+
+ if (displayed) {
+ if (!covered) {
+ if (attrs.width == LayoutParams.FILL_PARENT
+ && attrs.height == LayoutParams.FILL_PARENT) {
+ covered = true;
+ }
+ }
+ if (w.mOrientationChanging) {
+ if (w.mDrawPending || w.mCommitDrawPending) {
+ orientationChangeComplete = false;
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Orientation continue waiting for draw in " + w);
+ } else {
+ w.mOrientationChanging = false;
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Orientation change complete in " + w);
+ }
+ }
+ if (w.mAppToken != null) {
+ w.mAppToken.hasVisible = true;
+ }
+ }
+ } else if (w.mOrientationChanging) {
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Orientation change skips hidden " + w);
+ w.mOrientationChanging = false;
+ }
+
+ final boolean canBeSeen = w.isDisplayedLw();
+
+ if (someoneLosingFocus && w == mCurrentFocus && canBeSeen) {
+ focusDisplayed = true;
+ }
+
+ // Update effect.
+ if (!obscured) {
+ if (w.mSurface != null &&
+ (attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
+ holdScreen = w.mSession;
+ }
+ if (w.isFullscreenOpaque(dw, dh)) {
+ // This window completely covers everything behind it,
+ // so we want to leave all of them as unblurred (for
+ // performance reasons).
+ obscured = true;
+ } else if (canBeSeen && !obscured &&
+ (attrFlags&FLAG_BLUR_BEHIND|FLAG_DIM_BEHIND) != 0) {
+ if (localLOGV) Log.v(TAG, "Win " + w
+ + ": blurring=" + blurring
+ + " obscured=" + obscured
+ + " displayed=" + displayed);
+ if (!dimming && (attrFlags&FLAG_DIM_BEHIND) != 0) {
+ //Log.i(TAG, "DIM BEHIND: " + w);
+ dimming = true;
+ mDimShown = true;
+ if (mDimSurface == null) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ + mDimSurface + ": CREATE");
+ try {
+ mDimSurface = new Surface(mFxSession, 0,
+ -1, 16, 16,
+ PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating Dim surface", e);
+ }
+ }
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ + mDimSurface + ": SHOW pos=(0,0) (" +
+ dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
+ if (mDimSurface != null) {
+ try {
+ mDimSurface.setPosition(0, 0);
+ mDimSurface.setSize(dw, dh);
+ mDimSurface.setLayer(w.mAnimLayer-1);
+ mDimSurface.setAlpha(attrs.dimAmount);
+ mDimSurface.show();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure showing dim surface", e);
+ }
+ }
+ }
+ if (!blurring && (attrFlags&FLAG_BLUR_BEHIND) != 0) {
+ //Log.i(TAG, "BLUR BEHIND: " + w);
+ blurring = true;
+ mBlurShown = true;
+ if (mBlurSurface == null) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR "
+ + mBlurSurface + ": CREATE");
+ try {
+ mBlurSurface = new Surface(mFxSession, 0,
+ -1, 16, 16,
+ PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_BLUR);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating Blur surface", e);
+ }
+ }
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR "
+ + mBlurSurface + ": SHOW pos=(0,0) (" +
+ dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
+ if (mBlurSurface != null) {
+ mBlurSurface.setPosition(0, 0);
+ mBlurSurface.setSize(dw, dh);
+ mBlurSurface.setLayer(w.mAnimLayer-2);
+ try {
+ mBlurSurface.show();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure showing blur surface", e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!dimming && mDimShown) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface
+ + ": HIDE");
+ try {
+ mDimSurface.hide();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Illegal argument exception hiding dim surface");
+ }
+ mDimShown = false;
+ }
+
+ if (!blurring && mBlurShown) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface
+ + ": HIDE");
+ try {
+ mBlurSurface.hide();
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Illegal argument exception hiding blur surface");
+ }
+ mBlurShown = false;
+ }
+
+ if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION");
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unhandled exception in Window Manager", e);
+ }
+
+ Surface.closeTransaction();
+
+ if (DEBUG_ORIENTATION && mDisplayFrozen) Log.v(TAG,
+ "With display frozen, orientationChangeComplete="
+ + orientationChangeComplete);
+ if (orientationChangeComplete) {
+ if (mWindowsFreezingScreen) {
+ mWindowsFreezingScreen = false;
+ mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
+ }
+ if (mAppsFreezingScreen == 0) {
+ stopFreezingDisplayLocked();
+ }
+ }
+
+ i = mResizingWindows.size();
+ if (i > 0) {
+ do {
+ i--;
+ WindowState win = mResizingWindows.get(i);
+ try {
+ win.mClient.resized(win.mFrame.width(),
+ win.mFrame.height(), win.mDrawPending);
+ } catch (RemoteException e) {
+ win.mOrientationChanging = false;
+ }
+ } while (i > 0);
+ mResizingWindows.clear();
+ }
+
+ // Destroy the surface of any windows that are no longer visible.
+ i = mDestroySurface.size();
+ if (i > 0) {
+ do {
+ i--;
+ WindowState win = mDestroySurface.get(i);
+ win.mDestroying = false;
+ win.destroySurfaceLocked();
+ } while (i > 0);
+ mDestroySurface.clear();
+ }
+
+ // Time to remove any exiting applications?
+ for (i=0; i<mExitingAppTokens.size(); i++) {
+ AppWindowToken token = mExitingAppTokens.get(i);
+ if (!token.hasVisible && !mClosingApps.contains(token)) {
+ mAppTokens.remove(token);
+ mExitingAppTokens.remove(i);
+ i--;
+ //dump();
+ }
+ }
+
+ if (focusDisplayed) {
+ mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
+ }
+ if (animating) {
+ requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
+ }
+ mQueue.setHoldScreenLocked(holdScreen != null);
+ if (holdScreen != mHoldingScreenOn) {
+ mHoldingScreenOn = holdScreen;
+ Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
+ mH.sendMessage(m);
+ }
+ }
+
+ void requestAnimationLocked(long delay) {
+ if (!mAnimationPending) {
+ mAnimationPending = true;
+ mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay);
+ }
+ }
+
+ /**
+ * Have the surface flinger show a surface, robustly dealing wit
+ * error conditions. In particular, if there is not enough memory
+ * to show the surface, then we will try to get rid of other surfaces
+ * in order to succeed.
+ *
+ * @return Returns true if the surface was successfully shown.
+ */
+ boolean showSurfaceRobustlyLocked(WindowState win) {
+ try {
+ if (win.mSurface != null) {
+ win.mSurface.show();
+ }
+ return true;
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure showing surface " + win.mSurface + " in " + win);
+ }
+
+ reclaimSomeSurfaceMemoryLocked(win, "show");
+
+ return false;
+ }
+
+ void reclaimSomeSurfaceMemoryLocked(WindowState win, String operation) {
+ final Surface surface = win.mSurface;
+
+ EventLog.writeEvent(LOG_WM_NO_SURFACE_MEMORY, win.toString(),
+ win.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.
+ int N = mWindows.size();
+ boolean leakedSurface = false;
+ Log.i(TAG, "Out of memory for surface! Looking for leaks...");
+ for (int i=0; i<N; i++) {
+ WindowState ws = (WindowState)mWindows.get(i);
+ if (ws.mSurface != null) {
+ if (!mSessions.contains(ws.mSession)) {
+ Log.w(TAG, "LEAKED SURFACE (session doesn't exist): "
+ + ws + " surface=" + ws.mSurface
+ + " token=" + win.mToken
+ + " pid=" + ws.mSession.mPid
+ + " uid=" + ws.mSession.mUid);
+ ws.mSurface.clear();
+ ws.mSurface = null;
+ mForceRemoves.add(ws);
+ i--;
+ N--;
+ leakedSurface = true;
+ } else if (win.mAppToken != null && win.mAppToken.clientHidden) {
+ Log.w(TAG, "LEAKED SURFACE (app token hidden): "
+ + ws + " surface=" + ws.mSurface
+ + " token=" + win.mAppToken);
+ ws.mSurface.clear();
+ ws.mSurface = null;
+ leakedSurface = true;
+ }
+ }
+ }
+
+ boolean killedApps = false;
+ if (!leakedSurface) {
+ Log.w(TAG, "No leaked surfaces; killing applicatons!");
+ SparseIntArray pidCandidates = new SparseIntArray();
+ for (int i=0; i<N; i++) {
+ WindowState ws = (WindowState)mWindows.get(i);
+ if (ws.mSurface != null) {
+ pidCandidates.append(ws.mSession.mPid, ws.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.killPidsForMemory(pids)) {
+ 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.
+ Log.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
+ if (surface != null) {
+ surface.clear();
+ win.mSurface = null;
+ }
+
+ try {
+ win.mClient.dispatchGetNewSurface();
+ } catch (RemoteException e) {
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private boolean updateFocusedWindowLocked() {
+ WindowState newFocus = computeFocusedWindowLocked();
+ if (mCurrentFocus != newFocus) {
+ // This check makes sure that we don't already have the focus
+ // change message pending.
+ if (mLastFocus == mCurrentFocus) {
+ mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
+ }
+ if (localLOGV) Log.v(
+ TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
+ mCurrentFocus = newFocus;
+ mLosingFocus.remove(newFocus);
+ if (newFocus != null) {
+ mKeyWaiter.handleNewWindowLocked(newFocus);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private WindowState computeFocusedWindowLocked() {
+ WindowState result = null;
+ WindowState win;
+
+ int i = mWindows.size() - 1;
+ int nextAppIndex = mAppTokens.size()-1;
+ WindowToken nextApp = nextAppIndex >= 0
+ ? mAppTokens.get(nextAppIndex) : null;
+
+ while (i >= 0) {
+ win = (WindowState)mWindows.get(i);
+
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Looking for focus: " + i
+ + " = " + win
+ + ", flags=" + win.mAttrs.flags
+ + ", canReceive=" + win.canReceiveKeys());
+
+ AppWindowToken thisApp = win.mAppToken;
+
+ // If this window's application has been removed, just skip it.
+ if (thisApp != null && thisApp.removed) {
+ i--;
+ continue;
+ }
+
+ // If there is a focused app, don't allow focus to go to any
+ // windows below it. If this is an application window, step
+ // through the app tokens until we find its app.
+ if (thisApp != null && nextApp != null && thisApp != nextApp
+ && win.mAttrs.type != TYPE_APPLICATION_STARTING) {
+ int origAppIndex = nextAppIndex;
+ while (nextAppIndex > 0) {
+ if (nextApp == mFocusedApp) {
+ // Whoops, we are below the focused app... no focus
+ // for you!
+ if (localLOGV || DEBUG_FOCUS) Log.v(
+ TAG, "Reached focused app: " + mFocusedApp);
+ return null;
+ }
+ nextAppIndex--;
+ nextApp = mAppTokens.get(nextAppIndex);
+ if (nextApp == thisApp) {
+ break;
+ }
+ }
+ if (thisApp != nextApp) {
+ // Uh oh, the app token doesn't exist! This shouldn't
+ // happen, but if it does we can get totally hosed...
+ // so restart at the original app.
+ nextAppIndex = origAppIndex;
+ nextApp = mAppTokens.get(nextAppIndex);
+ }
+ }
+
+ // Dispatch to this window if it is wants key events.
+ if (win.canReceiveKeys()) {
+ if (DEBUG_FOCUS) Log.v(
+ TAG, "Found focus @ " + i + " = " + win);
+ result = win;
+ break;
+ }
+
+ i--;
+ }
+
+ return result;
+ }
+
+ private void startFreezingDisplayLocked() {
+ if (mDisplayFrozen) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now);
+ if (mFreezeGcPending != 0) {
+ if (now > (mFreezeGcPending+1000)) {
+ //Log.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000));
+ mH.removeMessages(H.FORCE_GC);
+ Runtime.getRuntime().gc();
+ mFreezeGcPending = now;
+ }
+ } else {
+ mFreezeGcPending = now;
+ }
+
+ mDisplayFrozen = true;
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+ mAppTransitionReady = true;
+ }
+
+ if (PROFILE_ORIENTATION) {
+ File file = new File("/data/system/frozen");
+ Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+ }
+ Surface.freezeDisplay(0);
+ }
+
+ private void stopFreezingDisplayLocked() {
+ if (!mDisplayFrozen) {
+ return;
+ }
+
+ mDisplayFrozen = false;
+ mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ if (PROFILE_ORIENTATION) {
+ Debug.stopMethodTracing();
+ }
+ Surface.unfreezeDisplay(0);
+
+ // Freezing the display also suspends key event delivery, to
+ // keep events from going astray while the display is reconfigured.
+ // Now that we're back, notify the key waiter that we're alive
+ // again and it should restart its timeouts.
+ synchronized (mKeyWaiter) {
+ mKeyWaiter.mWasFrozen = true;
+ mKeyWaiter.notifyAll();
+ }
+
+ // 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.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
+ 2000);
+ }
+
+ @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;
+ }
+
+ synchronized(mWindowMap) {
+ pw.println("Current Window Manager state:");
+ for (int i=mWindows.size()-1; i>=0; i--) {
+ WindowState w = (WindowState)mWindows.get(i);
+ pw.println(" Window #" + i + ":");
+ w.dump(pw, " ");
+ }
+ 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);
+ pw.println(" Remove #" + i + ":");
+ w.dump(pw, " ");
+ }
+ }
+ 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);
+ pw.println(" Destroy #" + i + ":");
+ w.dump(pw, " ");
+ }
+ }
+ 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);
+ pw.println(" Losing #" + i + ":");
+ w.dump(pw, " ");
+ }
+ }
+ if (mSessions.size() > 0) {
+ pw.println(" ");
+ pw.println(" All active sessions:");
+ Iterator<Session> it = mSessions.iterator();
+ while (it.hasNext()) {
+ Session s = it.next();
+ pw.println(" Session " + s);
+ s.dump(pw, " ");
+ }
+ }
+ if (mTokenMap.size() > 0) {
+ pw.println(" ");
+ pw.println(" All tokens:");
+ Iterator<WindowToken> it = mTokenMap.values().iterator();
+ while (it.hasNext()) {
+ WindowToken token = it.next();
+ pw.println(" Token " + token.token);
+ token.dump(pw, " ");
+ }
+ }
+ if (mAppTokens.size() > 0) {
+ pw.println(" ");
+ pw.println(" Application tokens in Z order:");
+ for (int i=mAppTokens.size()-1; i>=0; i--) {
+ WindowToken token = mAppTokens.get(i);
+ pw.println(" App Token #" + i + ":");
+ token.dump(pw, " ");
+ }
+ }
+ 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.println(" Finish Starting App Token #" + i + ":");
+ token.dump(pw, " ");
+ }
+ }
+ if (mExitingAppTokens.size() > 0) {
+ pw.println(" ");
+ pw.println(" Exiting application tokens:");
+ for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
+ WindowToken token = mExitingAppTokens.get(i);
+ pw.println(" Exiting App Token #" + i + ":");
+ token.dump(pw, " ");
+ }
+ }
+ pw.println(" ");
+ pw.println(" mCurrentFocus=" + mCurrentFocus);
+ pw.println(" mLastFocus=" + mLastFocus);
+ pw.println(" mFocusedApp=" + mFocusedApp);
+ pw.println(" mInTouchMode=" + mInTouchMode);
+ pw.println(" mSystemBooted=" + mSystemBooted
+ + " mDisplayEnabled=" + mDisplayEnabled);
+ pw.println(" mLayoutNeeded=" + mLayoutNeeded
+ + " mSurfacesChanged=" + mSurfacesChanged
+ + " mBlurShown=" + mBlurShown
+ + " mDimShown=" + mDimShown);
+ pw.println(" mDisplayFrozen=" + mDisplayFrozen
+ + " mWindowsFreezingScreen=" + mWindowsFreezingScreen
+ + " mAppsFreezingScreen=" + mAppsFreezingScreen);
+ pw.println(" mRotation=" + mRotation
+ + ", mForcedAppOrientation=" + mForcedAppOrientation
+ + ", mRequestedRotation=" + mRequestedRotation);
+ pw.println(" mAnimationPending=" + mAnimationPending
+ + " mWindowAnimationScale=" + mWindowAnimationScale
+ + " mTransitionWindowAnimationScale=" + mTransitionAnimationScale);
+ pw.println(" mNextAppTransition=0x"
+ + Integer.toHexString(mNextAppTransition)
+ + ", mAppTransitionReady=" + mAppTransitionReady
+ + ", mAppTransitionTimeout=" + mAppTransitionTimeout);
+ pw.println(" mStartingIconInTransition=" + mStartingIconInTransition
+ + ", mSkipAppTransitionAnimation=" + mSkipAppTransitionAnimation);
+ pw.println(" mOpeningApps=" + mOpeningApps);
+ pw.println(" mClosingApps=" + mClosingApps);
+ pw.println(" DisplayWidth=" + mDisplay.getWidth()
+ + " DisplayHeight=" + mDisplay.getHeight());
+ pw.println(" KeyWaiter state:");
+ pw.println(" mLastWin=" + mKeyWaiter.mLastWin
+ + " mLastBinder=" + mKeyWaiter.mLastBinder);
+ pw.println(" mFinished=" + mKeyWaiter.mFinished
+ + " mGotFirstWindow=" + mKeyWaiter.mGotFirstWindow
+ + " mEventDispatching" + mKeyWaiter.mEventDispatching
+ + " mTimeToSwitch=" + mKeyWaiter.mTimeToSwitch);
+ }
+ }
+
+ public void monitor() {
+ synchronized (mWindowMap) { }
+ synchronized (mKeyguardDisabled) { }
+ synchronized (mKeyWaiter) { }
+ }
+}