Dianne Hackborn 958b9adc08 AI 143899: am: CL 143896 Fix issue #1748954 and #1737952:
#1748954 (New status bar fades into all white background): FrameLayout wasn't updating its foreground drawable when its padding changed, which would happen as the status bar is shown and hidden.  To fix this I also ended up fixing a problem in the view debug stuff where we couldn't get a bitmap for a view that is the full screen size because it is too big...  actually I just went ahead and added another function to snapshot the view hierarchy which works a lot better for us anyway.
  #1737952 (Home screen icons overlap with the notification bar after exiting any camera app): Originally I punted this because it only happened in rare situations, but now that home is always portrait it happens a lot more so it is more important to fix.  This involved a few things to clean up hiding/showing the status bar:
  - We now determine when to hide and show it during layout, which allows us to do this at the time it is actually needed rather than during animation after we can actually catch it for the initial display of a window.  This required tweaking the layout API so the policy can request a second layout pass if needed.
  - When doing layout, we are now much more aggressive about skipping the layout of windows.  Basically anything that we know will be hidden in the near future is ignored for layout, so that it doesn't glitch as it is transfered out of the screen.  The theory being that it is better to leave it as it was originally placed while we are transitioning it out, than to switch it to something slightly more correct.
  Original author: hackbod
  Merged from: //branches/cupcake/...

Automated import of CL 143899
2009-03-31 18:00:36 -07:00

8820 lines
361 KiB
Java

/*
* 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.os.LocalPowerManager.LONG_TOUCH_EVENT;
import static android.os.LocalPowerManager.TOUCH_UP_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.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
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 static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.server.KeyInputQueue.QueuedEvent;
import com.android.server.am.BatteryStatsService;
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.BatteryStats;
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.Power;
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.ViewTreeObserver;
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_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_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;
/** Amount of time (in milliseconds) to animate the dim surface from one
* value to another, when no window animation is driving it.
*/
static final int DEFAULT_DIM_DURATION = 200;
/** Adjustment to time to perform a dim, to make it more dramatic.
*/
static final int DIM_DURATION_MULTIPLIER = 6;
static final int UPDATE_FOCUS_NORMAL = 0;
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
private static final String SYSTEM_SECURE = "ro.secure";
/**
* 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 boolean mHaveInputMethods;
final boolean mLimitedAlphaCompositing;
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>();
/**
* Window tokens that are in the process of exiting, but still
* on screen for animations.
*/
final ArrayList<WindowToken> mExitingTokens = 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;
IInputMethodManager mInputMethodManager;
SurfaceSession mFxSession;
Surface mDimSurface;
boolean mDimShown;
float mDimCurrentAlpha;
float mDimTargetAlpha;
float mDimDeltaPerMs;
long mLastDimAnimTime;
Surface mBlurSurface;
boolean mBlurShown;
int mTransactionSequence = 0;
final float[] mTmpFloats = new float[9];
boolean mSafeMode;
boolean mDisplayEnabled = false;
boolean mSystemBooted = false;
int mRotation = 0;
int mRequestedRotation = 0;
int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
int mLastRotationFlags;
ArrayList<IRotationWatcher> mRotationWatchers
= new ArrayList<IRotationWatcher>();
boolean mLayoutNeeded = true;
boolean mAnimationPending = false;
boolean mDisplayFrozen = false;
boolean mWindowsFreezingScreen = false;
long mFreezeGcPending = 0;
int mAppsFreezingScreen = 0;
// This is held as long as we have the screen frozen, to give us time to
// perform a rotation animation when turning off shows the lock screen which
// changes the orientation.
PowerManager.WakeLock mScreenFrozenLock;
// State management of app transitions. When we are preparing for a
// transition, mNextAppTransition will be the kind of transition to
// perform or TRANSIT_NONE if we are not waiting. If we are waiting,
// mOpeningApps and mClosingApps are the lists of tokens that will be
// made visible or hidden at the next transition.
int mNextAppTransition = WindowManagerPolicy.TRANSIT_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;
// This just indicates the window the input method is on top of, not
// necessarily the window its input is going to.
WindowState mInputMethodTarget = null;
WindowState mUpcomingInputMethodTarget = null;
boolean mInputMethodTargetWaitingAnim;
int mInputMethodAnimLayerAdjustment;
WindowState mInputMethodWindow = null;
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
AppWindowToken mFocusedApp = null;
PowerManagerService mPowerManager;
float mWindowAnimationScale = 1.0f;
float mTransitionAnimationScale = 1.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;
final Rect mTempRect = new Rect();
final Configuration mTempConfiguration = new Configuration();
public static WindowManagerService main(Context context,
PowerManagerService pm, boolean haveInputMethods) {
WMThread thr = new WMThread(context, pm, haveInputMethods);
thr.start();
synchronized (thr) {
while (thr.mService == null) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
}
return thr.mService;
}
static class WMThread extends Thread {
WindowManagerService mService;
private final Context mContext;
private final PowerManagerService mPM;
private final boolean mHaveInputMethods;
public WMThread(Context context, PowerManagerService pm,
boolean haveInputMethods) {
super("WindowManager");
mContext = context;
mPM = pm;
mHaveInputMethods = haveInputMethods;
}
public void run() {
Looper.prepare();
WindowManagerService s = new WindowManagerService(mContext, mPM,
mHaveInputMethods);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
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,
boolean haveInputMethods) {
mContext = context;
mHaveInputMethods = haveInputMethods;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
mPowerManager = pm;
mPowerManager.setPolicy(mPolicy);
PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mScreenFrozenLock = pmc.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
mActivityManager = ActivityManagerNative.getDefault();
mBatteryStats = BatteryStatsService.getService();
// Get persisted window scale setting
mWindowAnimationScale = Settings.System.getFloat(context.getContentResolver(),
Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
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 window 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, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
final ArrayList localmWindows = mWindows;
final int N = localmWindows.size();
final WindowState attached = win.mAttachedWindow;
int i;
if (attached == null) {
int tokenWindowsPos = token.windows.size();
if (token.appWindowToken != null) {
int index = tokenWindowsPos-1;
if (index >= 0) {
// If this application has existing windows, we
// simply place the new window on top of them... but
// keep the starting window on top.
if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
// Base windows go behind everything else.
placeWindowBefore(token.windows.get(0), win);
tokenWindowsPos = 0;
} else {
AppWindowToken atoken = win.mAppToken;
if (atoken != null &&
token.windows.get(index) == atoken.startingWindow) {
placeWindowBefore(token.windows.get(index), win);
tokenWindowsPos--;
} else {
int newIdx = findIdxBasedOnAppTokens(win);
if(newIdx != -1) {
//there is a window above this one associated with the same
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
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);
}
if (addToToken) {
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) {
if (addToToken) {
token.windows.add(i, win);
}
placeWindowBefore(
wSublayer >= 0 ? attached : w, win);
break;
}
} else {
// For positive sublayers, we go above all windows
// in the same sublayer.
if (wSublayer > sublayer) {
if (addToToken) {
token.windows.add(i, win);
}
placeWindowBefore(w, win);
break;
}
}
}
if (i >= NA) {
if (addToToken) {
token.windows.add(win);
}
if (sublayer < 0) {
placeWindowBefore(attached, win);
} else {
placeWindowAfter(largestSublayer >= 0
? windowWithLargestSublayer
: attached,
win);
}
}
}
if (win.mAppToken != null && addToToken) {
win.mAppToken.allAppWindows.add(win);
}
}
static boolean canBeImeTarget(WindowState w) {
final int fl = w.mAttrs.flags
& (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) {
return w.isVisibleOrAdding();
}
return false;
}
int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
final ArrayList localmWindows = mWindows;
final int N = localmWindows.size();
WindowState w = null;
int i = N;
while (i > 0) {
i--;
w = (WindowState)localmWindows.get(i);
//Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x"
// + Integer.toHexString(w.mAttrs.flags));
if (canBeImeTarget(w)) {
//Log.i(TAG, "Putting input method here!");
// Yet more tricksyness! If this window is a "starting"
// window, we do actually want to be on top of it, but
// it is not -really- where input will go. So if the caller
// is not actually looking to move the IME, look down below
// for a real window to target...
if (!willMove
&& w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& i > 0) {
WindowState wb = (WindowState)localmWindows.get(i-1);
if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
i--;
w = wb;
}
}
break;
}
}
mUpcomingInputMethodTarget = w;
if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target="
+ w + " willMove=" + willMove);
if (willMove && w != null) {
final WindowState curTarget = mInputMethodTarget;
if (curTarget != null && curTarget.mAppToken != null) {
// Now some fun for dealing with window animations that
// modify the Z order. We need to look at all windows below
// the current target that are in this app, finding the highest
// visible one in layering.
AppWindowToken token = curTarget.mAppToken;
WindowState highestTarget = null;
int highestPos = 0;
if (token.animating || token.animation != null) {
int pos = 0;
pos = localmWindows.indexOf(curTarget);
while (pos >= 0) {
WindowState win = (WindowState)localmWindows.get(pos);
if (win.mAppToken != token) {
break;
}
if (!win.mRemoved) {
if (highestTarget == null || win.mAnimLayer >
highestTarget.mAnimLayer) {
highestTarget = win;
highestPos = pos;
}
}
pos--;
}
}
if (highestTarget != null) {
if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition="
+ mNextAppTransition + " " + highestTarget
+ " animating=" + highestTarget.isAnimating()
+ " layer=" + highestTarget.mAnimLayer
+ " new layer=" + w.mAnimLayer);
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
// If we are currently setting up for an animation,
// hold everything until we can find out what will happen.
mInputMethodTargetWaitingAnim = true;
mInputMethodTarget = highestTarget;
return highestPos + 1;
} else if (highestTarget.isAnimating() &&
highestTarget.mAnimLayer > w.mAnimLayer) {
// If the window we are currently targeting is involved
// with an animation, and it is on top of the next target
// we will be over, then hold off on moving until
// that is done.
mInputMethodTarget = highestTarget;
return highestPos + 1;
}
}
}
}
//Log.i(TAG, "Placing input method @" + (i+1));
if (w != null) {
if (willMove) {
RuntimeException e = new RuntimeException();
e.fillInStackTrace();
if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+ mInputMethodTarget + " to " + w, e);
mInputMethodTarget = w;
if (w.mAppToken != null) {
setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
} else {
setInputMethodAnimLayerAdjustment(0);
}
}
return i+1;
}
if (willMove) {
RuntimeException e = new RuntimeException();
e.fillInStackTrace();
if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+ mInputMethodTarget + " to null", e);
mInputMethodTarget = null;
setInputMethodAnimLayerAdjustment(0);
}
return -1;
}
void addInputMethodWindowToListLocked(WindowState win) {
int pos = findDesiredInputMethodWindowIndexLocked(true);
if (pos >= 0) {
win.mTargetAppToken = mInputMethodTarget.mAppToken;
mWindows.add(pos, win);
moveInputMethodDialogsLocked(pos+1);
return;
}
win.mTargetAppToken = null;
addWindowToListInOrderLocked(win, true);
moveInputMethodDialogsLocked(pos);
}
void setInputMethodAnimLayerAdjustment(int adj) {
if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj);
mInputMethodAnimLayerAdjustment = adj;
WindowState imw = mInputMethodWindow;
if (imw != null) {
imw.mAnimLayer = imw.mLayer + adj;
if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw
+ " anim layer: " + imw.mAnimLayer);
int wi = imw.mChildWindows.size();
while (wi > 0) {
wi--;
WindowState cw = (WindowState)imw.mChildWindows.get(wi);
cw.mAnimLayer = cw.mLayer + adj;
if (DEBUG_LAYERS) Log.v(TAG, "IM win " + cw
+ " anim layer: " + cw.mAnimLayer);
}
}
int di = mInputMethodDialogs.size();
while (di > 0) {
di --;
imw = mInputMethodDialogs.get(di);
imw.mAnimLayer = imw.mLayer + adj;
if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw
+ " anim layer: " + imw.mAnimLayer);
}
}
private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
int wpos = mWindows.indexOf(win);
if (wpos >= 0) {
if (wpos < interestingPos) interestingPos--;
mWindows.remove(wpos);
int NC = win.mChildWindows.size();
while (NC > 0) {
NC--;
WindowState cw = (WindowState)win.mChildWindows.get(NC);
int cpos = mWindows.indexOf(cw);
if (cpos >= 0) {
if (cpos < interestingPos) interestingPos--;
mWindows.remove(cpos);
}
}
}
return interestingPos;
}
private void reAddWindowToListInOrderLocked(WindowState win) {
addWindowToListInOrderLocked(win, false);
// This is a hack to get all of the child windows added as well
// at the right position. Child windows should be rare and
// this case should be rare, so it shouldn't be that big a deal.
int wpos = mWindows.indexOf(win);
if (wpos >= 0) {
mWindows.remove(wpos);
reAddWindowLocked(wpos, win);
}
}
void logWindowList(String prefix) {
int N = mWindows.size();
while (N > 0) {
N--;
Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N));
}
}
void moveInputMethodDialogsLocked(int pos) {
ArrayList<WindowState> dialogs = mInputMethodDialogs;
final int N = dialogs.size();
if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos);
for (int i=0; i<N; i++) {
pos = tmpRemoveWindowLocked(pos, dialogs.get(i));
}
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "Window list w/pos=" + pos);
logWindowList(" ");
}
if (pos >= 0) {
final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
if (pos < mWindows.size()) {
WindowState wp = (WindowState)mWindows.get(pos);
if (wp == mInputMethodWindow) {
pos++;
}
}
if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos);
for (int i=0; i<N; i++) {
WindowState win = dialogs.get(i);
win.mTargetAppToken = targetAppToken;
pos = reAddWindowLocked(pos, win);
}
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "Final window list:");
logWindowList(" ");
}
return;
}
for (int i=0; i<N; i++) {
WindowState win = dialogs.get(i);
win.mTargetAppToken = null;
reAddWindowToListInOrderLocked(win);
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "No IM target, final list:");
logWindowList(" ");
}
}
}
boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) {
final WindowState imWin = mInputMethodWindow;
final int DN = mInputMethodDialogs.size();
if (imWin == null && DN == 0) {
return false;
}
int imPos = findDesiredInputMethodWindowIndexLocked(true);
if (imPos >= 0) {
// In this case, the input method windows are to be placed
// immediately above the window they are targeting.
// First check to see if the input method windows are already
// located here, and contiguous.
final int N = mWindows.size();
WindowState firstImWin = imPos < N
? (WindowState)mWindows.get(imPos) : null;
// Figure out the actual input method window that should be
// at the bottom of their stack.
WindowState baseImWin = imWin != null
? imWin : mInputMethodDialogs.get(0);
if (baseImWin.mChildWindows.size() > 0) {
WindowState cw = (WindowState)baseImWin.mChildWindows.get(0);
if (cw.mSubLayer < 0) baseImWin = cw;
}
if (firstImWin == baseImWin) {
// The windows haven't moved... but are they still contiguous?
// First find the top IM window.
int pos = imPos+1;
while (pos < N) {
if (!((WindowState)mWindows.get(pos)).mIsImWindow) {
break;
}
pos++;
}
pos++;
// Now there should be no more input method windows above.
while (pos < N) {
if (((WindowState)mWindows.get(pos)).mIsImWindow) {
break;
}
pos++;
}
if (pos >= N) {
// All is good!
return false;
}
}
if (imWin != null) {
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "Moving IM from " + imPos);
logWindowList(" ");
}
imPos = tmpRemoveWindowLocked(imPos, imWin);
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "List after moving with new pos " + imPos + ":");
logWindowList(" ");
}
imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
reAddWindowLocked(imPos, imWin);
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "List after moving IM to " + imPos + ":");
logWindowList(" ");
}
if (DN > 0) moveInputMethodDialogsLocked(imPos+1);
} else {
moveInputMethodDialogsLocked(imPos);
}
} else {
// In this case, the input method windows go in a fixed layer,
// because they aren't currently associated with a focus window.
if (imWin != null) {
if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos);
tmpRemoveWindowLocked(0, imWin);
imWin.mTargetAppToken = null;
reAddWindowToListInOrderLocked(imWin);
if (DEBUG_INPUT_METHOD) {
Log.v(TAG, "List with no IM target:");
logWindowList(" ");
}
if (DN > 0) moveInputMethodDialogsLocked(-1);;
} else {
moveInputMethodDialogsLocked(-1);;
}
}
if (needAssignLayers) {
assignLayersLocked();
}
return true;
}
void adjustInputMethodDialogsLocked() {
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
}
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
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(null, 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;
}
if (attrs.type == TYPE_INPUT_METHOD) {
Log.w(TAG, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
token = new WindowToken(attrs.token, -1, false);
addToken = true;
} else if (attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type <= LAST_APPLICATION_WINDOW) {
AppWindowToken atoken = token.appWindowToken;
if (atoken == null) {
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;
}
} else if (attrs.type == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Log.w(TAG, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
}
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;
}
boolean imMayMove = true;
if (attrs.type == TYPE_INPUT_METHOD) {
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);
imMayMove = false;
} else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
adjustInputMethodDialogsLocked();
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);
}
win.mEnterAnimationPending = true;
mPolicy.getContentInsetHintLw(attrs, outContentInsets);
if (mInTouchMode) {
res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;
}
if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) {
res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;
}
boolean focusChanged = false;
if (win.canReceiveKeys()) {
if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
== true) {
imMayMove = false;
}
}
if (imMayMove) {
moveInputMethodWindowsIfNeededLocked(false);
}
assignLayersLocked();
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
//dump();
if (focusChanged) {
if (mCurrentFocus != null) {
mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
}
}
if (localLOGV) Log.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
}
// sendNewConfiguration() checks caller permissions so we must call it with
// privilege. updateOrientationFromAppTokens() clears and resets the caller
// identity anyway, so it's safe to just clear & restore around this whole
// block.
final long origId = Binder.clearCallingIdentity();
if (reportNewConfig) {
sendNewConfiguration();
} else {
// Update Orientation after adding a window, only if the window needs to be
// displayed right away
if (win.isVisibleOrAdding()) {
if (updateOrientationFromAppTokens(null, null) != null) {
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);
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
// First, see if we need to run an animation. If we do, we have
// to hold off on removing the window until the animation is done.
// If the display is frozen, just remove immediately, since the
// animation wouldn't be seen.
if (win.mSurface != null && !mDisplayFrozen) {
// If we are not currently running the exit animation, we
// need to see about starting one.
if (wasVisible=win.isWinVisibleLw()) {
int transit = WindowManagerPolicy.TRANSIT_EXIT;
if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
// Try starting an animation.
if (applyAnimationLocked(win, transit, false)) {
win.mExiting = true;
}
}
if (win.mExiting || win.isAnimating()) {
// The exit animation is running... wait for it!
//Log.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
mLayoutNeeded = true;
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
//dump();
Binder.restoreCallingIdentity(origId);
return;
}
}
removeWindowInnerLocked(session, win);
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible && computeForcedAppOrientationLocked()
!= mForcedAppOrientation) {
mH.sendMessage(mH.obtainMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION));
}
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
Binder.restoreCallingIdentity(origId);
}
private void removeWindowInnerLocked(Session session, WindowState win) {
mKeyWaiter.releasePendingPointerLocked(win.mSession);
mKeyWaiter.releasePendingTrackballLocked(win.mSession);
win.mRemoved = true;
if (mInputMethodTarget == win) {
moveInputMethodWindowsIfNeededLocked(false);
}
mPolicy.removeWindowLw(win);
win.removeLocked();
mWindowMap.remove(win.mClient.asBinder());
mWindows.remove(win);
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
} else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.remove(win);
}
final WindowToken token = win.mToken;
final AppWindowToken atoken = win.mAppToken;
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 (!token.explicit) {
mTokenMap.remove(token.token);
mTokenList.remove(token);
} else if (atoken != null) {
atoken.firstWindowDrawn = false;
}
}
if (atoken != null) {
if (atoken.startingWindow == win) {
atoken.startingWindow = null;
} else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
// If this is the last window and we had requested a starting
// transition window, well there is no point now.
atoken.startingData = null;
} else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
// If this is the last window except for a starting transition
// window, we need to get rid of the starting transition.
if (DEBUG_STARTING_WINDOW) {
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);
}
}
void setInsetsWindow(Session session, IWindow client,
int touchableInsets, Rect contentInsets,
Rect visibleInsets) {
long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client);
if (w != null) {
w.mGivenInsetsPending = false;
w.mGivenContentInsets.set(contentInsets);
w.mGivenVisibleInsets.set(visibleInsets);
w.mTouchableInsets = touchableInsets;
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
public void getWindowDisplayFrame(Session session, IWindow client,
Rect outDisplayFrame) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client);
if (win == null) {
outDisplayFrame.setEmpty();
return;
}
outDisplayFrame.set(win.mDisplayFrame);
}
}
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
Surface outSurface) {
boolean displayed = false;
boolean inTouchMode;
Configuration newConfig = null;
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);
}
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
flagChanges = win.mAttrs.flags ^= attrs.flags;
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 imMayMove = (flagChanges&(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
win.mRelayoutCalled = true;
final int oldVisibility = win.mViewVisibility;
win.mViewVisibility = viewVisibility;
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
displayed = !win.isVisibleLw();
if (win.mExiting) {
win.mExiting = false;
win.mAnimation = null;
}
if (win.mDestroying) {
win.mDestroying = false;
mDestroySurface.remove(win);
}
if (oldVisibility == View.GONE) {
win.mEnterAnimationPending = true;
}
if (displayed && win.mSurface != null && !win.mDrawPending
&& !win.mCommitDrawPending && !mDisplayFrozen) {
applyEnterAnimationLocked(win);
}
if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
// To change the format, we need to re-build the surface.
win.destroySurfaceLocked();
displayed = true;
}
try {
Surface surface = win.createSurfaceLocked();
if (surface != null) {
outSurface.copyFrom(surface);
} 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;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& mInputMethodWindow == null) {
mInputMethodWindow = win;
imMayMove = true;
}
} else {
win.mEnterAnimationPending = false;
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.isWinVisibleLw() &&
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 {
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
}
win.destroySurfaceLocked();
}
}
}
outSurface.clear();
}
if (focusMayChange) {
//System.out.println("Focus may change: " + win.mAttrs.getTitle());
if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
imMayMove = false;
}
//System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
}
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean assignLayers = false;
if (imMayMove) {
if (moveInputMethodWindowsIfNeededLocked(false)) {
assignLayers = true;
}
}
mLayoutNeeded = true;
win.mGivenInsetsPending = insetsPending;
if (assignLayers) {
assignLayersLocked();
}
newConfig = updateOrientationFromAppTokensLocked(null, null);
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
outFrame.set(win.mFrame);
outContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
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);
inTouchMode = mInTouchMode;
}
if (newConfig != null) {
sendNewConfiguration();
}
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.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
// an animation of the same type, then just leave that one alone.
return true;
}
// Only apply an animation if the display isn't frozen. If it is
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
if (!mDisplayFrozen) {
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);
win.mAnimationIsEntrance = isEntrance;
}
} else {
win.clearAnimation();
}
return win.mAnimation != null;
}
private Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
int anim = 0;
Context context = mContext;
if (animAttr >= 0) {
AttributeCache.Entry ent = getCachedAnimations(lp);
if (ent != null) {
context = ent.context;
anim = ent.array.getResourceId(animAttr, 0);
}
}
if (anim != 0) {
return AnimationUtils.loadAnimation(context, anim);
}
return null;
}
private 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 addWindowToken(IBinder token, int type) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addWindowToken()")) {
return;
}
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.get(token);
if (wtoken != null) {
Log.w(TAG, "Attempted to add existing input method token: " + token);
return;
}
wtoken = new WindowToken(token, type, true);
mTokenMap.put(token, wtoken);
mTokenList.add(wtoken);
}
}
public void removeWindowToken(IBinder token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"removeWindowToken()")) {
return;
}
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.remove(token);
mTokenList.remove(wtoken);
if (wtoken != null) {
boolean delayed = false;
if (!wtoken.hidden) {
wtoken.hidden = true;
final int N = wtoken.windows.size();
boolean changed = false;
for (int i=0; i<N; i++) {
WindowState win = wtoken.windows.get(i);
if (win.isAnimating()) {
delayed = true;
}
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
KeyWaiter.RETURN_NOTHING);
changed = true;
}
}
if (changed) {
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
}
if (delayed) {
mExitingTokens.add(wtoken);
}
}
} else {
Log.w(TAG, "Attempted to remove non-existing token: " + token);
}
}
Binder.restoreCallingIdentity(origId);
}
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 int getOrientationFromWindowsLocked() {
int pos = mWindows.size() - 1;
while (pos >= 0) {
WindowState wtoken = (WindowState) mWindows.get(pos);
pos--;
if (wtoken.mAppToken != null) {
// We hit an application window. so the orientation will be determined by the
// app window. No point in continuing further.
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
if (!wtoken.isVisibleLw()) {
continue;
}
int req = wtoken.mAttrs.screenOrientation;
if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
(req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
continue;
} else {
return req;
}
}
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
public int getOrientationFromAppTokensLocked() {
int pos = mAppTokens.size() - 1;
int curGroup = 0;
int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean haveGroup = false;
boolean lastFullscreen = false;
while (pos >= 0) {
AppWindowToken wtoken = mAppTokens.get(pos);
pos--;
// if we're about to tear down this window, don't use it for orientation
if (!wtoken.hidden && wtoken.hiddenRequested) {
continue;
}
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, and the last app was
// full screen, then we'll stick with the
// user's orientation.
if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
&& lastFullscreen) {
return lastOrientation;
}
}
int or = wtoken.requestedOrientation;
// If this application is fullscreen, then just take whatever
// orientation it has and ignores whatever is under it.
lastFullscreen = wtoken.appFullscreen;
if (lastFullscreen) {
return or;
}
// 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) {
return or;
}
}
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
public Configuration updateOrientationFromAppTokens(
Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
Configuration config;
long ident = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded);
}
if (config != null) {
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
Binder.restoreCallingIdentity(ident);
return config;
}
/*
* The orientation is computed from non-application windows first. If none of
* the non-application windows specify orientation, the orientation is computed from
* application tokens.
* @see android.view.IWindowManager#updateOrientationFromAppTokens(
* android.os.IBinder)
*/
Configuration updateOrientationFromAppTokensLocked(
Configuration appConfig, IBinder freezeThisOneIfNeeded) {
boolean changed = false;
long ident = Binder.clearCallingIdentity();
try {
int req = computeForcedAppOrientationLocked();
if (req != mForcedAppOrientation) {
changed = true;
mForcedAppOrientation = req;
//send a message to Policy indicating orientation change to take
//action like disabling/enabling sensors etc.,
mPolicy.setCurrentOrientationLw(req);
}
if (changed) {
changed = setRotationUncheckedLocked(
WindowManagerPolicy.USE_LAST_ROTATION,
mLastRotationFlags & (~Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE));
if (changed) {
if (freezeThisOneIfNeeded != null) {
AppWindowToken wtoken = findAppWindowToken(
freezeThisOneIfNeeded);
if (wtoken != null) {
startAppFreezingScreenLocked(wtoken,
ActivityInfo.CONFIG_ORIENTATION);
}
}
return computeNewConfigurationLocked();
}
}
// No obvious action we need to take, but if our current
// state mismatches the activity maanager's, update it
if (appConfig != null) {
mTempConfiguration.setToDefaults();
if (computeNewConfigurationLocked(mTempConfiguration)) {
if (appConfig.diff(mTempConfiguration) != 0) {
Log.i(TAG, "Config changed: " + mTempConfiguration);
return new Configuration(mTempConfiguration);
}
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
return null;
}
int computeForcedAppOrientationLocked() {
int req = getOrientationFromWindowsLocked();
if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
req = getOrientationFromAppTokensLocked();
}
return req;
}
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(UPDATE_FOCUS_NORMAL);
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 int getPendingAppTransition() {
return mNextAppTransition;
}
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.mRootToken = wtoken;
startingWindow.mAppToken = wtoken;
mWindows.remove(startingWindow);
ttoken.windows.remove(startingWindow);
ttoken.allAppWindows.remove(startingWindow);
addWindowToListInOrderLocked(startingWindow, true);
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();
}
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
Binder.restoreCallingIdentity(origId);
return;
} else if (ttoken.startingData != null) {
// The previous app was getting ready to show a
// starting window, but hasn't yet done so. Steal it!
if (DEBUG_STARTING_WINDOW) 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);
boolean runningAppAnimation = false;
if (transit != WindowManagerPolicy.TRANSIT_NONE) {
if (wtoken.animation == sDummyAnimation) {
wtoken.animation = null;
}
applyAnimationLocked(wtoken, lp, transit, visible);
changed = true;
if (wtoken.animation != null) {
delayed = runningAppAnimation = true;
}
}
for (int i=0; i<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.isVisibleNow()) {
if (!runningAppAnimation) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_ENTER, true);
}
changed = true;
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
}
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;
swin.mPolicyVisibilityAfterAnim = false;
}
}
if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken
+ ": hidden=" + wtoken.hidden + " hiddenRequested="
+ wtoken.hiddenRequested);
if (changed && performLayout) {
mLayoutNeeded = true;
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
performLayoutAndPlaceSurfacesLocked();
}
}
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) {
// set the token aside because it has an active animation to be finished
mExitingAppTokens.add(wtoken);
}
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(UPDATE_FOCUS_NORMAL);
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 reAddWindowLocked(int index, WindowState win) {
final int NCW = win.mChildWindows.size();
boolean added = false;
for (int j=0; j<NCW; j++) {
WindowState cwin = (WindowState)win.mChildWindows.get(j);
if (!added && cwin.mSubLayer >= 0) {
mWindows.add(index, win);
index++;
added = true;
}
mWindows.add(index, cwin);
index++;
}
if (!added) {
mWindows.add(index, win);
index++;
}
return index;
}
private final int reAddAppWindowsLocked(int index, WindowToken token) {
final int NW = token.windows.size();
for (int i=0; i<NW; i++) {
index = reAddWindowLocked(index, token.windows.get(i));
}
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();
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
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);
}
}
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
//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 boolean hasKeys(int[] keycodes, boolean[] keyExists) {
return KeyInputQueue.hasKeys(keycodes, keyExists);
}
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.isVisibleLw() && !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,
mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
}
public void setInTouchMode(boolean mode) {
synchronized(mWindowMap) {
mInTouchMode = mode;
}
}
public void setRotation(int rotation,
boolean alwaysSendConfiguration, int animFlags) {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"setRotation()")) {
return;
}
setRotationUnchecked(rotation, alwaysSendConfiguration, animFlags);
}
public void setRotationUnchecked(int rotation,
boolean alwaysSendConfiguration, int animFlags) {
if(DEBUG_ORIENTATION) Log.v(TAG,
"alwaysSendConfiguration set to "+alwaysSendConfiguration);
long origId = Binder.clearCallingIdentity();
boolean changed;
synchronized(mWindowMap) {
changed = setRotationUncheckedLocked(rotation, animFlags);
}
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, int animFlags) {
boolean changed;
if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) {
rotation = mRequestedRotation;
} else {
mRequestedRotation = rotation;
mLastRotationFlags = animFlags;
}
if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from " + rotation);
rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation,
mRotation, mDisplayEnabled);
if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation);
changed = mDisplayEnabled && mRotation != rotation;
if (changed) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"Rotation changed to " + rotation
+ " from " + mRotation
+ " (forceApp=" + mForcedAppOrientation
+ ", req=" + mRequestedRotation + ")");
mRotation = rotation;
mWindowsFreezingScreen = true;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
2000);
startFreezingDisplayLocked();
Log.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
mQueue.setOrientation(rotation);
if (mDisplayEnabled) {
Surface.setOrientation(0, rotation, animFlags);
}
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;
}
/*
* Instruct the Activity Manager to fetch the current configuration and broadcast
* that to config-changed listeners if appropriate.
*/
void sendNewConfiguration() {
try {
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
}
public Configuration computeNewConfiguration() {
synchronized (mWindowMap) {
return computeNewConfigurationLocked();
}
}
Configuration computeNewConfigurationLocked() {
Configuration config = new Configuration();
if (!computeNewConfigurationLocked(config)) {
return null;
}
Log.i(TAG, "Config 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;
}
boolean computeNewConfigurationLocked(Configuration config) {
if (mDisplay == null) {
return false;
}
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;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
mPolicy.adjustConfigurationLw(config);
return true;
}
// -------------------------------------------------------------
// 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 : TOUCH_UP_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 LONG_TOUCH_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 {
if (action == MotionEvent.ACTION_DOWN) {
WindowState out = mKeyWaiter.mOutsideTouchTargets;
if (out != null) {
MotionEvent oev = MotionEvent.obtain(ev);
oev.setAction(MotionEvent.ACTION_OUTSIDE);
do {
final Rect frame = out.mFrame;
oev.offsetLocation(-(float)frame.left, -(float)frame.top);
try {
out.mClient.dispatchPointer(oev, eventTime);
} catch (android.os.RemoteException e) {
Log.i(TAG, "WINDOW DIED during outside motion dispatch: " + out);
}
oev.offsetLocation((float)frame.left, (float)frame.top);
out = out.mNextOutsideTouch;
} while (out != null);
mKeyWaiter.mOutsideTouchTargets = null;
}
}
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);
}
// NOSHIP extra state logging
mKeyWaiter.recordDispatchState(event, focus);
// END NOSHIP
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, KeyEvent.FLAG_FROM_SYSTEM);
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 pointer (touch) 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 {
// NOSHIP debugging
public class DispatchState {
private KeyEvent event;
private WindowState focus;
private long time;
private WindowState lastWin;
private IBinder lastBinder;
private boolean finished;
private boolean gotFirstWindow;
private boolean eventDispatching;
private long timeToSwitch;
private boolean wasFrozen;
private boolean focusPaused;
private WindowState curFocus;
DispatchState(KeyEvent theEvent, WindowState theFocus) {
focus = theFocus;
event = theEvent;
time = System.currentTimeMillis();
// snapshot KeyWaiter state
lastWin = mLastWin;
lastBinder = mLastBinder;
finished = mFinished;
gotFirstWindow = mGotFirstWindow;
eventDispatching = mEventDispatching;
timeToSwitch = mTimeToSwitch;
wasFrozen = mWasFrozen;
curFocus = mCurrentFocus;
// cache the paused state at ctor time as well
if (theFocus == null || theFocus.mToken == null) {
Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!");
focusPaused = false;
} else {
focusPaused = theFocus.mToken.paused;
}
}
public String toString() {
return "{{" + event + " to " + focus + " @ " + time
+ " lw=" + lastWin + " lb=" + lastBinder
+ " fin=" + finished + " gfw=" + gotFirstWindow
+ " ed=" + eventDispatching + " tts=" + timeToSwitch
+ " wf=" + wasFrozen + " fp=" + focusPaused
+ " mcf=" + mCurrentFocus + "}}";
}
};
private DispatchState mDispatchState = null;
public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) {
mDispatchState = new DispatchState(theEvent, theFocus);
}
// END NOSHIP
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;
// Windows above the target who would like to receive an "outside"
// touch event for any down events outside of them.
WindowState mOutsideTouchTargets;
/**
* 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
+ " mCurrentFocus=" + mCurrentFocus);
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
+ " target=" + targetWin + " mLW=" + mLastWin
+ " mLB=" + mLastBinder + " fin=" + mFinished
+ " mCurrentFocus=" + mCurrentFocus);
} 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>"));
// NOSHIP debugging
Log.w(TAG, "Dispatch state: " + mDispatchState);
Log.w(TAG, "Current state: " + new DispatchState(nextKey, targetWin));
// END NOSHIP
//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) {
mOutsideTouchTargets = null;
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;
final Rect tmpRect = mTempRect;
for (int i=N-1; i>=0; i--) {
WindowState child = (WindowState)windows.get(i);
//Log.i(TAG, "Checking dispatch to: " + child);
final int flags = child.mAttrs.flags;
if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
if (topErrWindow == null) {
topErrWindow = child;
}
}
if (!child.isVisibleLw()) {
//Log.i(TAG, "Not visible!");
continue;
}
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
//Log.i(TAG, "Not touchable!");
if ((flags & WindowManager.LayoutParams
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
child.mNextOutsideTouch = mOutsideTouchTargets;
mOutsideTouchTargets = child;
}
continue;
}
tmpRect.set(child.mFrame);
if (child.mTouchableInsets == ViewTreeObserver
.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
// The touch is inside of the window if it is
// inside the frame, AND the content part of that
// frame that was given by the application.
tmpRect.left += child.mGivenContentInsets.left;
tmpRect.top += child.mGivenContentInsets.top;
tmpRect.right -= child.mGivenContentInsets.right;
tmpRect.bottom -= child.mGivenContentInsets.bottom;
} else if (child.mTouchableInsets == ViewTreeObserver
.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
// The touch is inside of the window if it is
// inside the frame, AND the visible part of that
// frame that was given by the application.
tmpRect.left += child.mGivenVisibleInsets.left;
tmpRect.top += child.mGivenVisibleInsets.top;
tmpRect.right -= child.mGivenVisibleInsets.right;
tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
}
final int touchFlags = flags &
(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
if (tmpRect.contains(x, y) || touchFlags == 0) {
//Log.i(TAG, "Using this target!");
if (!screenWasOff || (flags &
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
mMotionTarget = child;
} else {
//Log.i(TAG, "Waking, skip!");
mMotionTarget = null;
}
break;
}
if ((flags & WindowManager.LayoutParams
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
child.mNextOutsideTouch = mOutsideTouchTargets;
mOutsideTouchTargets = child;
//Log.i(TAG, "Adding to outside target list: " + child);
}
}
// 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);
mQueue.recycleEvent(qev);
if (win != null && returnWhat == RETURN_PENDING_POINTER) {
res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
}
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;
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) {
// The new window is above the old; finish pending input to the last
// window and start directing it to the new one.
mLastWin.mToken.paused = false;
doFinishedKeyLocked(true); // does a notifyAll()
}
// Either the new window is lower, so there is no need to wake key waiters,
// or we just finished key input to the previous window, which implicitly
// notified the key waiters. In both cases, we don't need to issue the
// notification here.
return;
}
// Now that we've put a new window state in place, make the event waiter
// take notice and retarget its attentions.
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.isVisibleLw()) {
// 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,
"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 {
mPolicy.screenOnStoppedLw();
mHoldingScreen.release();
}
}
}
};
public boolean detectSafeMode() {
mSafeMode = mPolicy.detectSafeMode();
return mSafeMode;
}
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;
}
try {
mBatteryStats.noteInputEvent();
} catch (RemoteException e) {
// Ignore
}
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(KeyEvent.changeTimeRepeat(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 IInputMethodClient mClient;
final IInputContext mInputContext;
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(IInputMethodClient client, IInputContext inputContext) {
mClient = client;
mInputContext = inputContext;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
synchronized (mWindowMap) {
if (mInputMethodManager == null && mHaveInputMethods) {
IBinder b = ServiceManager.getService(
Context.INPUT_METHOD_SERVICE);
mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
}
}
long ident = Binder.clearCallingIdentity();
try {
// Note: it is safe to call in to the input method manager
// here because we are not holding our lock.
if (mInputMethodManager != null) {
mInputMethodManager.addClient(client, inputContext,
mUid, mPid);
} else {
client.setUsingInputMethod(false);
}
client.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
// The caller has died, so we can just forget about this.
try {
if (mInputMethodManager != null) {
mInputMethodManager.removeClient(client);
}
} catch (RemoteException ee) {
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@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() {
// Note: it is safe to call in to the input method manager
// here because we are not holding our lock.
try {
if (mInputMethodManager != null) {
mInputMethodManager.removeClient(mClient);
}
} catch (RemoteException e) {
}
synchronized(mWindowMap) {
mClientDead = true;
killSessionLocked();
}
}
public int add(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
return addWindow(this, window, attrs, viewVisibility, outContentInsets);
}
public void remove(IWindow window) {
removeWindow(this, window);
}
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
boolean insetsPending, Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Surface outSurface) {
return relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, insetsPending,
outFrame, outContentInsets, outVisibleInsets, outSurface);
}
public void setTransparentRegion(IWindow window, Region region) {
setTransparentRegionWindow(this, window, region);
}
public void setInsets(IWindow window, int touchableInsets,
Rect contentInsets, Rect visibleInsets) {
setInsetsWindow(this, window, touchableInsets, contentInsets,
visibleInsets);
}
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
getWindowDisplayFrame(this, window, outDisplayFrame);
}
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 setInTouchMode(boolean mode) {
synchronized(mWindowMap) {
mInTouchMode = mode;
}
}
public boolean getInTouchMode() {
synchronized(mWindowMap) {
return mInTouchMode;
}
}
public boolean performHapticFeedback(IWindow window, int effectId,
boolean always) {
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
return mPolicy.performHapticFeedbackLw(
windowForClientLocked(this, window), effectId, always);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
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;
WindowToken mRootToken;
AppWindowToken mAppToken;
AppWindowToken mTargetAppToken;
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
final DeathRecipient mDeathRecipient;
final WindowState mAttachedWindow;
final ArrayList mChildWindows = new ArrayList();
final int mBaseLayer;
final int mSubLayer;
final boolean mLayoutAttached;
final boolean mIsImWindow;
int mViewVisibility;
boolean mPolicyVisibility = true;
boolean mPolicyVisibilityAfterAnim = 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;
WindowState mNextOutsideTouch;
// Actual frame shown on-screen (may be modified by animation)
final Rect mShownFrame = new Rect();
final Rect mLastShownFrame = new Rect();
/**
* Insets that determine the actually visible area
*/
final Rect mVisibleInsets = new Rect();
final Rect mLastVisibleInsets = new Rect();
boolean mVisibleInsetsChanged;
/**
* Insets that are covered by system windows
*/
final Rect mContentInsets = new Rect();
final Rect mLastContentInsets = new Rect();
boolean mContentInsetsChanged;
/**
* Set to true if we are waiting for this window to receive its
* given internal insets before laying out other windows based on it.
*/
boolean mGivenInsetsPending;
/**
* These are the content insets that were given during layout for
* this window, to be applied to windows behind it.
*/
final Rect mGivenContentInsets = new Rect();
/**
* These are the visible insets that were given during layout for
* this window, to be applied to windows behind it.
*/
final Rect mGivenVisibleInsets = new Rect();
/**
* Flag indicating whether the touchable region should be adjusted by
* the visible insets; if false the area outside the visible insets is
* NOT touchable, so we must use those to adjust the frame during hit
* tests.
*/
int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
// 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();
final Rect mDisplayFrame = new Rect();
final Rect mContentFrame = new Rect();
final Rect mVisibleFrame = 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;
boolean mLocalAnimating;
Animation mAnimation;
boolean mAnimationIsEntrance;
boolean mHasTransformation;
boolean mHasLocalTransformation;
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;
// Is this window now (or just being) removed?
boolean mRemoved;
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;
mLayoutAttached = false;
mIsImWindow = false;
mBaseLayer = 0;
mSubLayer = 0;
return;
}
mDeathRecipient = deathRecipient;
if ((mAttrs.type >= FIRST_SUB_WINDOW &&
mAttrs.type <= 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);
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
|| attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
} 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;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
}
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;
}
mRootToken = appToken;
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(Rect pf, Rect df, Rect cf, Rect vf) {
mHaveFrame = true;
final int pw = pf.right-pf.left;
final int ph = pf.bottom-pf.top;
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.set(pf);
final Rect display = mDisplayFrame;
display.set(df);
final Rect content = mContentFrame;
content.set(cf);
final Rect visible = mVisibleFrame;
visible.set(vf);
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.
Gravity.applyDisplay(mAttrs.gravity, df, frame);
// Make sure the content and visible frames are inside of the
// final window frame.
if (content.left < frame.left) content.left = frame.left;
if (content.top < frame.top) content.top = frame.top;
if (content.right > frame.right) content.right = frame.right;
if (content.bottom > frame.bottom) content.bottom = frame.bottom;
if (visible.left < frame.left) visible.left = frame.left;
if (visible.top < frame.top) visible.top = frame.top;
if (visible.right > frame.right) visible.right = frame.right;
if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
final Rect contentInsets = mContentInsets;
contentInsets.left = content.left-frame.left;
contentInsets.top = content.top-frame.top;
contentInsets.right = frame.right-content.right;
contentInsets.bottom = frame.bottom-content.bottom;
final Rect visibleInsets = mVisibleInsets;
visibleInsets.left = visible.left-frame.left;
visibleInsets.top = visible.top-frame.top;
visibleInsets.right = frame.right-visible.right;
visibleInsets.bottom = frame.bottom-visible.bottom;
if (localLOGV) {
//if ("com.google.android.youtube".equals(mAttrs.packageName)
// && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
Log.v(TAG, "Resolving (mRequestedWidth="
+ mRequestedWidth + ", mRequestedheight="
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ "): frame=" + mFrame.toShortString()
+ " ci=" + contentInsets.toShortString()
+ " vi=" + visibleInsets.toShortString());
//}
}
}
public Rect getFrameLw() {
return mFrame;
}
public Rect getShownFrameLw() {
return mShownFrame;
}
public Rect getDisplayFrameLw() {
return mDisplayFrame;
}
public Rect getContentFrameLw() {
return mContentFrame;
}
public Rect getVisibleFrameLw() {
return mVisibleFrame;
}
public boolean getGivenInsetsPendingLw() {
return mGivenInsetsPending;
}
public Rect getGivenContentInsetsLw() {
return mGivenContentInsets;
}
public Rect getGivenVisibleInsetsLw() {
return mGivenVisibleInsets;
}
public WindowManager.LayoutParams getAttrs() {
return mAttrs;
}
public int getSurfaceLayer() {
return mLayer;
}
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;
mLocalAnimating = false;
mAnimation = anim;
mAnimation.restrictDuration(MAX_ANIMATION_DURATION);
mAnimation.scaleCurrentDuration(mWindowAnimationScale);
}
public void clearAnimation() {
if (mAnimation != null) {
mAnimating = true;
mLocalAnimating = false;
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;
mHasLocalTransformation = true;
if (!mLocalAnimating) {
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);
mLocalAnimating = true;
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 (more) {
// we're not done!
return true;
}
if (DEBUG_ANIM) Log.v(
TAG, "Finished animation in " + this +
" @ " + currentTime);
mAnimation = null;
//WindowManagerService.this.dump();
}
mHasLocalTransformation = false;
if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
&& mAppToken.hasTransformation) {
// When our app token is animating, we kind-of pretend like
// we are as well. Note the mLocalAnimating mAnimationIsEntrance
// part of this check means that we will only do this if
// our window is not currently exiting, or it is not
// locally animating itself. The idea being that one that
// is exiting and doing a local animation should be removed
// once that animation is done.
mAnimating = true;
mHasTransformation = true;
mTransformation.clear();
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;
mLocalAnimating = true;
mAnimation = null;
}
if (!mAnimating && !mLocalAnimating) {
return false;
}
if (DEBUG_ANIM) Log.v(
TAG, "Animation done in " + this + ": exiting=" + mExiting
+ ", reportedVisible="
+ (mAppToken != null ? mAppToken.reportedVisible : false));
mAnimating = false;
mLocalAnimating = false;
mAnimation = null;
mAnimLayer = mLayer;
if (mIsImWindow) {
mAnimLayer += mInputMethodAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
+ " anim layer: " + mAnimLayer);
mHasTransformation = false;
mHasLocalTransformation = false;
mPolicyVisibility = mPolicyVisibilityAfterAnim;
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 = mHasLocalTransformation;
Transformation attachedTransformation =
(mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
? mAttachedWindow.mTransformation : null;
Transformation appTransformation =
(mAppToken != null && mAppToken.hasTransformation)
? mAppToken.transformation : null;
if (selfTransformation || attachedTransformation != null
|| appTransformation != null) {
// 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 != null) {
tmpMatrix.preConcat(attachedTransformation.getMatrix());
}
if (appTransformation != null) {
tmpMatrix.preConcat(appTransformation.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 (!mLimitedAlphaCompositing
|| (!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 != null) {
mShownAlpha *= attachedTransformation.getAlpha();
}
if (appTransformation != null) {
mShownAlpha *= appTransformation.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, or its app token has been hidden.
*/
public boolean isVisibleLw() {
final AppWindowToken atoken = mAppToken;
return mSurface != null && mPolicyVisibility && !mAttachedHidden
&& (atoken == null || !atoken.hiddenRequested)
&& !mExiting && !mDestroying;
}
/**
* Is this window visible, ignoring its app token? 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.
*/
public boolean isWinVisibleLw() {
final AppWindowToken atoken = mAppToken;
return mSurface != null && mPolicyVisibility && !mAttachedHidden
&& (atoken == null || !atoken.hiddenRequested || atoken.animating)
&& !mExiting && !mDestroying;
}
/**
* The same as isVisible(), but follows the current hidden state of
* the associated app token, not the pending requested hidden state.
*/
boolean isVisibleNow() {
return mSurface != null && mPolicyVisibility && !mAttachedHidden
&& !mRootToken.hidden && !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;
final boolean animating = atoken != null ? atoken.animating : false;
return mSurface != null && mPolicyVisibility && !mDestroying
&& ((!mAttachedHidden && !mRootToken.hidden)
|| mAnimating || animating);
}
/** 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, boolean onlyOpaque) {
if (mSurface == null) {
return false;
}
if (mAppToken != null && !mAppToken.appFullscreen) {
return false;
}
if (onlyOpaque && mAttrs.format != PixelFormat.OPAQUE) {
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 boolean showLw(boolean doAnimation) {
if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) {
mPolicyVisibility = true;
mPolicyVisibilityAfterAnim = true;
if (doAnimation) {
applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
}
requestAnimationLocked(0);
return true;
}
return false;
}
public boolean hideLw(boolean doAnimation) {
boolean current = doAnimation ? mPolicyVisibilityAfterAnim
: mPolicyVisibility;
if (current) {
if (doAnimation) {
applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
if (mAnimation == null) {
doAnimation = false;
}
}
if (doAnimation) {
mPolicyVisibilityAfterAnim = false;
} else {
mPolicyVisibilityAfterAnim = false;
mPolicyVisibility = false;
}
requestAnimationLocked(0);
return true;
}
return false;
}
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
+ " mLayoutAttached=" + mLayoutAttached
+ " mIsImWindow=" + mIsImWindow);
pw.println(prefix + "mBaseLayer=" + mBaseLayer
+ " mSubLayer=" + mSubLayer
+ " mAnimLayer=" + mLayer + "+"
+ (mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
: (mAppToken != null ? mAppToken.animLayerAdjustment : 0))
+ "=" + mAnimLayer
+ " mLastLayer=" + mLastLayer);
pw.println(prefix + "mSurface=" + mSurface);
pw.println(prefix + "mToken=" + mToken);
pw.println(prefix + "mRootToken=" + mRootToken);
pw.println(prefix + "mAppToken=" + mAppToken);
pw.println(prefix + "mTargetAppToken=" + mTargetAppToken);
pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
+ " mPolicyVisibility=" + mPolicyVisibility
+ " (after=" + mPolicyVisibilityAfterAnim
+ ") mAttachedHidden=" + mAttachedHidden
+ " mLastHidden=" + mLastHidden
+ " mHaveFrame=" + mHaveFrame);
pw.println(prefix + "Requested w=" + mRequestedWidth + " h=" + mRequestedHeight
+ " x=" + mReqXPos + " y=" + mReqYPos);
pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString()
+ " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString()
+ " mTouchableInsets=" + mTouchableInsets
+ " pending=" + mGivenInsetsPending);
pw.println(prefix + "mShownFrame=" + mShownFrame.toShortString()
+ " last=" + mLastShownFrame.toShortString());
pw.println(prefix + "mFrame=" + mFrame.toShortString()
+ " last=" + mLastFrame.toShortString());
pw.println(prefix + "mContainingFrame=" + mContainingFrame.toShortString()
+ " mDisplayFrame=" + mDisplayFrame.toShortString());
pw.println(prefix + "mContentFrame=" + mContentFrame.toShortString()
+ " mVisibleFrame=" + mVisibleFrame.toShortString());
pw.println(prefix + "mContentInsets=" + mContentInsets.toShortString()
+ " last=" + mLastContentInsets.toShortString()
+ " mVisibleInsets=" + mVisibleInsets.toShortString()
+ " last=" + mLastVisibleInsets.toShortString());
pw.println(prefix + "mShownAlpha=" + mShownAlpha
+ " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha);
pw.println(prefix + "mAnimating=" + mAnimating
+ " mLocalAnimating=" + mLocalAnimating
+ " mAnimationIsEntrance=" + mAnimationIsEntrance
+ " mAnimation=" + mAnimation);
pw.println(prefix + "XForm: has=" + mHasTransformation
+ " " + mTransformation.toShortString());
pw.println(prefix + "mDrawPending=" + mDrawPending
+ " mCommitDrawPending=" + mCommitDrawPending
+ " mReadyToShow=" + mReadyToShow
+ " mHasDrawn=" + mHasDrawn);
pw.println(prefix + "mExiting=" + mExiting
+ " mRemoveOnExit=" + mRemoveOnExit
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
+ " mAppFreezing=" + mAppFreezing);
}
@Override
public String toString() {
return "Window{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}";
}
}
// -------------------------------------------------------------
// Window Token State
// -------------------------------------------------------------
class WindowToken {
// The actual token.
final IBinder token;
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
// Set if this token was explicitly added by a client, so should
// not be removed when all windows are removed.
final boolean explicit;
// 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;
// Should this token's windows be hidden?
boolean hidden;
// Temporary for finding which tokens no longer have visible windows.
boolean hasVisible;
WindowToken(IBinder _token, int type, boolean _explicit) {
token = _token;
windowType = type;
explicit = _explicit;
}
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + this);
pw.println(prefix + "token=" + token);
pw.println(prefix + "windows=" + windows);
pw.println(prefix + "windowType=" + windowType + " hidden=" + hidden
+ " hasVisible=" + hasVisible);
}
@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;
// 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;
// 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(),
WindowManager.LayoutParams.TYPE_APPLICATION, true);
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();
final int adj = animLayerAdjustment;
for (int i=0; i<N; i++) {
WindowState w = allAppWindows.get(i);
w.mAnimLayer = w.mLayer + adj;
if (DEBUG_LAYERS) Log.v(TAG, "Updating layer " + w + ": "
+ w.mAnimLayer);
if (w == mInputMethodTarget) {
setInputMethodAnimLayerAdjustment(adj);
}
}
}
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 (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) {
moveInputMethodWindowsIfNeededLocked(true);
}
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 + "hiddenRequested=" + hiddenRequested
+ " clientHidden=" + clientHidden
+ " willBeHidden=" + willBeHidden
+ " reportedVisible=" + reportedVisible);
pw.println(prefix + "paused=" + paused
+ " freezingScreen=" + freezingScreen);
pw.println(prefix + "numInterestingWindows=" + numInterestingWindows
+ " numDrawnWindows=" + numDrawnWindows
+ " inPendingTransaction=" + inPendingTransaction
+ " allDrawn=" + allDrawn);
pw.println(prefix + "animating=" + animating
+ " animation=" + animation);
pw.println(prefix + "animLayerAdjustment=" + animLayerAdjustment
+ " transformation=" + transformation.toShortString());
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 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;
public static final int COMPUTE_AND_SEND_NEW_CONFIGURATION = 18;
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;
if (lastFocus == newFocus) {
// Focus is not changing, so nothing to do.
return;
}
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 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;
}
case COMPUTE_AND_SEND_NEW_CONFIGURATION: {
if (updateOrientationFromAppTokens(null, null) != null) {
sendNewConfiguration();
}
break;
}
}
}
}
// -------------------------------------------------------------
// IWindowManager API
// -------------------------------------------------------------
public IWindowSession openSession(IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
return new Session(client, inputContext);
}
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
synchronized (mWindowMap) {
// The focus for the client is the window immediately below
// where we would place the input method window.
int idx = findDesiredInputMethodWindowIndexLocked(false);
WindowState imFocus;
if (idx > 0) {
imFocus = (WindowState)mWindows.get(idx-1);
if (imFocus != null) {
if (imFocus.mSession.mClient != null &&
imFocus.mSession.mClient.asBinder() == client.asBinder()) {
return true;
}
}
}
}
return false;
}
// -------------------------------------------------------------
// 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 (session != null && 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() {
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 || w.mIsImWindow) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
w.mLayer = curLayer;
}
if (w.mTargetAppToken != null) {
w.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
} else if (w.mAppToken != null) {
w.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
} else {
w.mAnimLayer = w.mLayer;
}
if (w.mIsImWindow) {
w.mAnimLayer += mInputMethodAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
+ w.mAnimLayer);
//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!");
}
Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout");
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 repeats = 0;
int i;
// FIRST LOOP: Perform a layout, if needed.
while (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);
// Don't do layout of a window if it is not visible, or
// soon won't be visible, to avoid wasting time and funky
// changes while a window is animating away.
final AppWindowToken atoken = win.mAppToken;
final boolean gone = win.mViewVisibility == View.GONE
|| !win.mRelayoutCalled
|| win.mRootToken.hidden
|| (atoken != null && atoken.hiddenRequested)
|| !win.mPolicyVisibility
|| win.mAttachedHidden
|| win.mExiting || win.mDestroying;
// 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.mLayoutAttached) {
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.mLayoutAttached) {
if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
|| !win.mHaveFrame) {
mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
}
}
}
if (!mPolicy.finishLayoutLw()) {
mLayoutNeeded = false;
} else if (repeats > 2) {
Log.w(TAG, "Layout repeat aborted after too many iterations");
mLayoutNeeded = false;
} else {
repeats++;
}
}
}
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 tokens.
for (i=mExitingTokens.size()-1; i>=0; i--) {
mExitingTokens.get(i).hasVisible = false;
}
// Initialize state of exiting applications.
for (i=mExitingAppTokens.size()-1; i>=0; i--) {
mExitingAppTokens.get(i).hasVisible = false;
}
// SECOND LOOP: Execute animations and update visibility of windows.
boolean orientationChangeComplete = true;
Session holdScreen = null;
float screenBrightness = -1;
boolean focusDisplayed = false;
boolean animating = false;
Surface.openTransaction();
try {
boolean restart;
do {
final int transactionSequence = ++mTransactionSequence;
// Update animations of all applications, including those
// associated with exiting/removed apps
boolean tokensAnimating = false;
final int NAT = mAppTokens.size();
for (i=0; i<NAT; i++) {
if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
tokensAnimating = true;
}
}
final int NEAT = mExitingAppTokens.size();
for (i=0; i<NEAT; i++) {
if (mExitingAppTokens.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;
moveInputMethodWindowsIfNeededLocked(true);
performLayoutLockedInner();
updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
restart = true;
}
}
} while (restart);
// THIRD LOOP: Update the surfaces of all windows.
final boolean someoneLosingFocus = mLosingFocus.size() != 0;
boolean obscured = false;
boolean blurring = false;
boolean dimming = false;
boolean covered = false;
boolean syswin = false;
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) {
w.mContentInsetsChanged =
!w.mLastContentInsets.equals(w.mContentInsets);
w.mVisibleInsetsChanged =
!w.mLastVisibleInsets.equals(w.mVisibleInsets);
if (!w.mLastFrame.equals(w.mFrame)
|| w.mContentInsetsChanged
|| w.mVisibleInsetsChanged) {
w.mLastFrame.set(w.mFrame);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
// 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.mSurface != null) {
w.mToken.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);
}
}
w.mToken.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) {
if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
holdScreen = w.mSession;
}
if (!syswin && w.mAttrs.screenBrightness >= 0
&& screenBrightness < 0) {
screenBrightness = w.mAttrs.screenBrightness;
}
if (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
|| attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD
|| attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR) {
syswin = true;
}
}
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 ((attrFlags&FLAG_DIM_BEHIND) != 0) {
if (!dimming) {
//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.show();
} catch (RuntimeException e) {
Log.w(TAG, "Failure showing dim surface", e);
}
}
}
mDimSurface.setLayer(w.mAnimLayer-1);
final float target = w.mExiting ? 0 : attrs.dimAmount;
if (mDimTargetAlpha != target) {
// If the desired dim level has changed, then
// start an animation to it.
mLastDimAnimTime = currentTime;
long duration = (w.mAnimating && w.mAnimation != null)
? w.mAnimation.computeDurationHint()
: DEFAULT_DIM_DURATION;
if (target > mDimTargetAlpha) {
// This is happening behind the activity UI,
// so we can make it run a little longer to
// give a stronger impression without disrupting
// the user.
duration *= DIM_DURATION_MULTIPLIER;
}
if (duration < 1) {
// Don't divide by zero
duration = 1;
}
mDimTargetAlpha = target;
mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha)
/ duration;
}
}
if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
if (!blurring) {
//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);
try {
mBlurSurface.show();
} catch (RuntimeException e) {
Log.w(TAG, "Failure showing blur surface", e);
}
}
}
mBlurSurface.setLayer(w.mAnimLayer-2);
}
}
}
}
if (!dimming && mDimShown) {
// Time to hide the dim surface... start fading.
if (mDimTargetAlpha != 0) {
mLastDimAnimTime = currentTime;
mDimTargetAlpha = 0;
mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION;
}
}
if (mDimShown && mLastDimAnimTime != 0) {
mDimCurrentAlpha += mDimDeltaPerMs
* (currentTime-mLastDimAnimTime);
boolean more = true;
if (mDisplayFrozen) {
// If the display is frozen, there is no reason to animate.
more = false;
} else if (mDimDeltaPerMs > 0) {
if (mDimCurrentAlpha > mDimTargetAlpha) {
more = false;
}
} else if (mDimDeltaPerMs < 0) {
if (mDimCurrentAlpha < mDimTargetAlpha) {
more = false;
}
} else {
more = false;
}
// Do we need to continue animating?
if (more) {
if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ mDimSurface + ": alpha=" + mDimCurrentAlpha);
mLastDimAnimTime = currentTime;
mDimSurface.setAlpha(mDimCurrentAlpha);
animating = true;
} else {
mDimCurrentAlpha = mDimTargetAlpha;
mLastDimAnimTime = 0;
if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ mDimSurface + ": final alpha=" + mDimCurrentAlpha);
mDimSurface.setAlpha(mDimCurrentAlpha);
if (!dimming) {
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.mLastContentInsets,
win.mLastVisibleInsets, win.mDrawPending);
win.mContentInsetsChanged = false;
win.mVisibleInsetsChanged = false;
} 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;
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
}
win.destroySurfaceLocked();
} while (i > 0);
mDestroySurface.clear();
}
// Time to remove any exiting tokens?
for (i=mExitingTokens.size()-1; i>=0; i--) {
WindowToken token = mExitingTokens.get(i);
if (!token.hasVisible) {
mExitingTokens.remove(i);
}
}
// Time to remove any exiting applications?
for (i=mExitingAppTokens.size()-1; i>=0; i--) {
AppWindowToken token = mExitingAppTokens.get(i);
if (!token.hasVisible && !mClosingApps.contains(token)) {
mAppTokens.remove(token);
mExitingAppTokens.remove(i);
}
}
if (focusDisplayed) {
mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
}
if (animating) {
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
mQueue.setHoldScreenLocked(holdScreen != null);
if (screenBrightness < 0 || screenBrightness > 1.0f) {
mPowerManager.setScreenBrightnessOverride(-1);
} else {
mPowerManager.setScreenBrightnessOverride((int)
(screenBrightness * Power.BRIGHTNESS_ON));
}
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 with
* 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(int mode) {
WindowState newFocus = computeFocusedWindowLocked();
if (mCurrentFocus != newFocus) {
// This check makes sure that we don't already have the focus
// change message pending.
mH.removeMessages(H.REPORT_FOCUS_CHANGE);
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
if (localLOGV) Log.v(
TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
final WindowState imWindow = mInputMethodWindow;
if (newFocus != imWindow && oldFocus != imWindow) {
if (moveInputMethodWindowsIfNeededLocked(
mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
mLayoutNeeded = true;
}
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner();
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
assignLayersLocked();
}
}
if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
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) {
// Freezing the display also suspends key event delivery, to
// keep events from going astray while the display is reconfigured.
// If someone has changed orientation again while the screen is
// still frozen, the events will continue to be blocked while the
// successive orientation change is processed. To prevent spurious
// ANRs, we reset the event dispatch timeout in this case.
synchronized (mKeyWaiter) {
mKeyWaiter.mWasFrozen = true;
}
return;
}
mScreenFrozenLock.acquire();
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);
// Reset the key delivery timeout on unfreeze, too. We force a wakeup here
// too because regular key delivery processing should resume immediately.
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);
mScreenFrozenLock.release();
}
@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 (mInputMethodDialogs.size() > 0) {
pw.println(" ");
pw.println(" Input method dialogs:");
for (int i=mInputMethodDialogs.size()-1; i>=0; i--) {
WindowState w = mInputMethodDialogs.get(i);
pw.println(" IM Dialog #" + i + ": " + w);
}
}
if (mPendingRemove.size() > 0) {
pw.println(" ");
pw.println(" Remove pending for:");
for (int i=mPendingRemove.size()-1; i>=0; i--) {
WindowState w = mPendingRemove.get(i);
pw.println(" Remove #" + i + ":");
w.dump(pw, " ");
}
}
if (mForceRemoves != null && mForceRemoves.size() > 0) {
pw.println(" ");
pw.println(" Windows force removing:");
for (int i=mForceRemoves.size()-1; i>=0; i--) {
WindowState w = mForceRemoves.get(i);
pw.println(" Removing #" + 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 (mTokenList.size() > 0) {
pw.println(" ");
pw.println(" Window token list:");
for (int i=0; i<mTokenList.size(); i++) {
pw.println(" WindowToken #" + i + ": " + mTokenList.get(i));
}
}
if (mAppTokens.size() > 0) {
pw.println(" ");
pw.println(" Application tokens in Z order:");
for (int i=mAppTokens.size()-1; i>=0; i--) {
pw.println(" AppWindowToken #" + i + ": " + mAppTokens.get(i));
}
}
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 (mExitingTokens.size() > 0) {
pw.println(" ");
pw.println(" Exiting tokens:");
for (int i=mExitingTokens.size()-1; i>=0; i--) {
WindowToken token = mExitingTokens.get(i);
pw.println(" Exiting 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(" mInputMethodTarget=" + mInputMethodTarget);
pw.println(" mInputMethodWindow=" + mInputMethodWindow);
pw.println(" mInTouchMode=" + mInTouchMode);
pw.println(" mSystemBooted=" + mSystemBooted
+ " mDisplayEnabled=" + mDisplayEnabled);
pw.println(" mLayoutNeeded=" + mLayoutNeeded
+ " mBlurShown=" + mBlurShown);
pw.println(" mDimShown=" + mDimShown
+ " current=" + mDimCurrentAlpha
+ " target=" + mDimTargetAlpha
+ " delta=" + mDimDeltaPerMs
+ " lastAnimTime=" + mLastDimAnimTime);
pw.println(" mInputMethodAnimLayerAdjustment="
+ mInputMethodAnimLayerAdjustment);
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) { }
}
}