blob: 2e16157fa294c4be9b868ae261396d748b4e6ea5 [file] [log] [blame]
/*
* 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;
}
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 i;
// FIRST LOOP: Perform a layout, if needed.
if (mLayoutNeeded) {
mPolicy.beginLayoutLw(dw, dh);
// First perform layout of any root windows (not attached
// to another window).
int topAttached = -1;
for (i = N-1; i >= 0; i--) {
WindowState win = (WindowState) mWindows.get(i);
boolean gone = win.mViewVisibility == View.GONE
|| !win.mRelayoutCalled
|| win.mRootToken.hidden;
// 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);
}
}
}
mPolicy.finishLayoutLw();
mLayoutNeeded = false;
}
}
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
final long currentTime = SystemClock.uptimeMillis();
final int dw = mDisplay.getWidth();
final int dh = mDisplay.getHeight();
final int N = mWindows.size();
int i;
// FIRST LOOP: Perform a layout, if needed.
performLayoutLockedInner();
if (mFxSession == null) {
mFxSession = new SurfaceSession();
}
if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION");
// Initialize state of exiting 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) { }
}
}