diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /services/java/com/android/server/WindowManagerService.java | |
download | frameworks_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.java | 7408 |
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) { } + } +} |