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