/*
|
* Copyright (C) 2016 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.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.INVALID_DISPLAY;
|
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
|
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
|
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
|
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
|
|
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
|
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
|
import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
|
import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
|
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
|
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
|
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
|
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
|
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
|
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
|
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
|
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
|
import static com.android.server.wm.WindowManagerService.logSurface;
|
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
|
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
|
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
|
|
import android.annotation.CallSuper;
|
import android.annotation.NonNull;
|
import android.content.res.Configuration;
|
import android.hardware.power.V1_0.PowerHint;
|
import android.os.Binder;
|
import android.os.Debug;
|
import android.os.Handler;
|
import android.os.IBinder;
|
import android.os.Looper;
|
import android.os.Message;
|
import android.os.PowerManager;
|
import android.os.RemoteException;
|
import android.os.Trace;
|
import android.os.UserHandle;
|
import android.util.ArraySet;
|
import android.util.EventLog;
|
import android.util.Slog;
|
import android.util.SparseIntArray;
|
import android.util.proto.ProtoOutputStream;
|
import android.view.Display;
|
import android.view.DisplayInfo;
|
import android.view.SurfaceControl;
|
import android.view.WindowManager;
|
|
import com.android.server.EventLogTags;
|
|
import java.io.PrintWriter;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.function.Consumer;
|
|
/** Root {@link WindowContainer} for the device. */
|
class RootWindowContainer extends WindowContainer<DisplayContent>
|
implements ConfigurationContainerListener {
|
private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
|
|
private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
|
private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
|
|
// TODO: Remove after object merge with RootActivityContainer.
|
private RootActivityContainer mRootActivityContainer;
|
|
private Object mLastWindowFreezeSource = null;
|
private Session mHoldScreen = null;
|
private float mScreenBrightness = -1;
|
private long mUserActivityTimeout = -1;
|
private boolean mUpdateRotation = false;
|
// Following variables are for debugging screen wakelock only.
|
// Last window that requires screen wakelock
|
WindowState mHoldScreenWindow = null;
|
// Last window that obscures all windows below
|
WindowState mObscuringWindow = null;
|
// Only set while traversing the default display based on its content.
|
// Affects the behavior of mirroring on secondary displays.
|
private boolean mObscureApplicationContentOnSecondaryDisplays = false;
|
|
private boolean mSustainedPerformanceModeEnabled = false;
|
private boolean mSustainedPerformanceModeCurrent = false;
|
|
// During an orientation change, we track whether all windows have rendered
|
// at the new orientation, and this will be false from changing orientation until that occurs.
|
// For seamless rotation cases this always stays true, as the windows complete their orientation
|
// changes 1 by 1 without disturbing global state.
|
boolean mOrientationChangeComplete = true;
|
boolean mWallpaperActionPending = false;
|
|
private final Handler mHandler;
|
|
private String mCloseSystemDialogsReason;
|
|
// The ID of the display which is responsible for receiving display-unspecified key and pointer
|
// events.
|
private int mTopFocusedDisplayId = INVALID_DISPLAY;
|
|
// Map from the PID to the top most app which has a focused window of the process.
|
final HashMap<Integer, AppWindowToken> mTopFocusedAppByProcess = new HashMap<>();
|
|
// Only a separate transaction until we separate the apply surface changes
|
// transaction from the global transaction.
|
private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
|
|
private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
|
if (w.mHasSurface) {
|
try {
|
w.mClient.closeSystemDialogs(mCloseSystemDialogsReason);
|
} catch (RemoteException e) {
|
}
|
}
|
};
|
|
private static final Consumer<WindowState> sRemoveReplacedWindowsConsumer = w -> {
|
final AppWindowToken aToken = w.mAppToken;
|
if (aToken != null) {
|
aToken.removeReplacedWindowIfNeeded(w);
|
}
|
};
|
|
RootWindowContainer(WindowManagerService service) {
|
super(service);
|
mHandler = new MyHandler(service.mH.getLooper());
|
}
|
|
void setRootActivityContainer(RootActivityContainer container) {
|
mRootActivityContainer = container;
|
if (container != null) {
|
container.registerConfigurationChangeListener(this);
|
}
|
}
|
|
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
|
mTopFocusedAppByProcess.clear();
|
boolean changed = false;
|
int topFocusedDisplayId = INVALID_DISPLAY;
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent dc = mChildren.get(i);
|
changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
|
final WindowState newFocus = dc.mCurrentFocus;
|
if (newFocus != null) {
|
final int pidOfNewFocus = newFocus.mSession.mPid;
|
if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
|
mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mAppToken);
|
}
|
if (topFocusedDisplayId == INVALID_DISPLAY) {
|
topFocusedDisplayId = dc.getDisplayId();
|
}
|
} else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
|
// The top-most display that has a focused app should still be the top focused
|
// display even when the app window is not ready yet (process not attached or
|
// window not added yet).
|
topFocusedDisplayId = dc.getDisplayId();
|
}
|
}
|
if (topFocusedDisplayId == INVALID_DISPLAY) {
|
topFocusedDisplayId = DEFAULT_DISPLAY;
|
}
|
if (mTopFocusedDisplayId != topFocusedDisplayId) {
|
mTopFocusedDisplayId = topFocusedDisplayId;
|
mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
|
mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
|
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
|
+ topFocusedDisplayId);
|
}
|
return changed;
|
}
|
|
DisplayContent getTopFocusedDisplayContent() {
|
final DisplayContent dc = getDisplayContent(mTopFocusedDisplayId);
|
return dc != null ? dc : getDisplayContent(DEFAULT_DISPLAY);
|
}
|
|
@Override
|
void onChildPositionChanged() {
|
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
|
!mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
|
}
|
|
DisplayContent getDisplayContent(int displayId) {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent current = mChildren.get(i);
|
if (current.getDisplayId() == displayId) {
|
return current;
|
}
|
}
|
return null;
|
}
|
|
DisplayContent createDisplayContent(final Display display, ActivityDisplay activityDisplay) {
|
final int displayId = display.getDisplayId();
|
|
// In select scenarios, it is possible that a DisplayContent will be created on demand
|
// rather than waiting for the controller. In this case, associate the controller and return
|
// the existing display.
|
final DisplayContent existing = getDisplayContent(displayId);
|
|
if (existing != null) {
|
existing.mAcitvityDisplay = activityDisplay;
|
existing.initializeDisplayOverrideConfiguration();
|
return existing;
|
}
|
|
final DisplayContent dc = new DisplayContent(display, mWmService, activityDisplay);
|
|
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
|
|
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
|
dc.initializeDisplayOverrideConfiguration();
|
|
if (mWmService.mDisplayManagerInternal != null) {
|
mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
|
displayId, dc.getDisplayInfo());
|
dc.configureDisplayPolicy();
|
}
|
|
mWmService.reconfigureDisplayLocked(dc);
|
|
return dc;
|
}
|
|
/**
|
* Called when DisplayWindowSettings values may change.
|
*/
|
void onSettingsRetrieved() {
|
final int numDisplays = mChildren.size();
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
final boolean changed = mWmService.mDisplayWindowSettings.updateSettingsForDisplay(
|
displayContent);
|
if (!changed) {
|
continue;
|
}
|
|
displayContent.initializeDisplayOverrideConfiguration();
|
mWmService.reconfigureDisplayLocked(displayContent);
|
|
// We need to update global configuration as well if config of default display has
|
// changed. Do it inline because ATMS#retrieveSettings() will soon update the
|
// configuration inline, which will overwrite the new windowing mode.
|
if (displayContent.isDefaultDisplay) {
|
final Configuration newConfig = mWmService.computeNewConfiguration(
|
displayContent.getDisplayId());
|
mWmService.mAtmService.updateConfigurationLocked(newConfig, null /* starting */,
|
false /* initLocale */);
|
}
|
}
|
}
|
|
boolean isLayoutNeeded() {
|
final int numDisplays = mChildren.size();
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
if (displayContent.isLayoutNeeded()) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
void getWindowsByName(ArrayList<WindowState> output, String name) {
|
int objectId = 0;
|
// See if this is an object ID.
|
try {
|
objectId = Integer.parseInt(name, 16);
|
name = null;
|
} catch (RuntimeException e) {
|
}
|
|
getWindowsByName(output, name, objectId);
|
}
|
|
private void getWindowsByName(ArrayList<WindowState> output, String name, int objectId) {
|
forAllWindows((w) -> {
|
if (name != null) {
|
if (w.mAttrs.getTitle().toString().contains(name)) {
|
output.add(w);
|
}
|
} else if (System.identityHashCode(w) == objectId) {
|
output.add(w);
|
}
|
}, true /* traverseTopToBottom */);
|
}
|
|
/**
|
* Returns true if the callingUid has any non-toast window currently visible to the user.
|
* Also ignores TYPE_APPLICATION_STARTING, since those windows don't belong to apps.
|
*/
|
boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
|
return forAllWindows(w ->
|
w.getOwningUid() == callingUid && w.mAttrs.type != TYPE_TOAST
|
&& w.mAttrs.type != TYPE_APPLICATION_STARTING && w.isVisibleNow(),
|
true /* traverseTopToBottom */);
|
}
|
|
/**
|
* Returns the app window token for the input binder if it exist in the system.
|
* NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
|
* AppWindowToken represents an activity which can only exist on one display.
|
*/
|
AppWindowToken getAppWindowToken(IBinder binder) {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent dc = mChildren.get(i);
|
final AppWindowToken atoken = dc.getAppWindowToken(binder);
|
if (atoken != null) {
|
return atoken;
|
}
|
}
|
return null;
|
}
|
|
/** Returns the window token for the input binder if it exist in the system. */
|
WindowToken getWindowToken(IBinder binder) {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent dc = mChildren.get(i);
|
final WindowToken wtoken = dc.getWindowToken(binder);
|
if (wtoken != null) {
|
return wtoken;
|
}
|
}
|
return null;
|
}
|
|
/** Returns the display object the input window token is currently mapped on. */
|
DisplayContent getWindowTokenDisplay(WindowToken token) {
|
if (token == null) {
|
return null;
|
}
|
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent dc = mChildren.get(i);
|
final WindowToken current = dc.getWindowToken(token.token);
|
if (current == token) {
|
return dc;
|
}
|
}
|
|
return null;
|
}
|
|
/**
|
* Set new display override config. If called for the default display, global configuration
|
* will also be updated.
|
*/
|
void setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration,
|
@NonNull DisplayContent displayContent) {
|
|
final Configuration currentConfig = displayContent.getRequestedOverrideConfiguration();
|
final boolean configChanged = currentConfig.diff(newConfiguration) != 0;
|
if (!configChanged) {
|
return;
|
}
|
|
displayContent.onRequestedOverrideConfigurationChanged(newConfiguration);
|
|
if (displayContent.getDisplayId() == DEFAULT_DISPLAY) {
|
// Override configuration of the default display duplicates global config. In this case
|
// we also want to update the global config.
|
setGlobalConfigurationIfNeeded(newConfiguration);
|
}
|
}
|
|
private void setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
|
final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
|
if (!configChanged) {
|
return;
|
}
|
onConfigurationChanged(newConfiguration);
|
}
|
|
@Override
|
public void onConfigurationChanged(Configuration newParentConfig) {
|
prepareFreezingTaskBounds();
|
super.onConfigurationChanged(newParentConfig);
|
}
|
|
private void prepareFreezingTaskBounds() {
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
mChildren.get(i).prepareFreezingTaskBounds();
|
}
|
}
|
|
TaskStack getStack(int windowingMode, int activityType) {
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final DisplayContent dc = mChildren.get(i);
|
final TaskStack stack = dc.getStack(windowingMode, activityType);
|
if (stack != null) {
|
return stack;
|
}
|
}
|
return null;
|
}
|
|
void setSecureSurfaceState(int userId, boolean disabled) {
|
forAllWindows((w) -> {
|
if (w.mHasSurface && userId == UserHandle.getUserId(w.mOwnerUid)) {
|
w.mWinAnimator.setSecureLocked(disabled);
|
}
|
}, true /* traverseTopToBottom */);
|
}
|
|
void updateHiddenWhileSuspendedState(final ArraySet<String> packages, final boolean suspended) {
|
forAllWindows((w) -> {
|
if (packages.contains(w.getOwningPackage())) {
|
w.setHiddenWhileSuspended(suspended);
|
}
|
}, false);
|
}
|
|
void updateAppOpsState() {
|
forAllWindows((w) -> {
|
w.updateAppOpsState();
|
}, false /* traverseTopToBottom */);
|
}
|
|
boolean canShowStrictModeViolation(int pid) {
|
final WindowState win = getWindow((w) -> w.mSession.mPid == pid && w.isVisibleLw());
|
return win != null;
|
}
|
|
void closeSystemDialogs(String reason) {
|
mCloseSystemDialogsReason = reason;
|
forAllWindows(mCloseSystemDialogsConsumer, false /* traverseTopToBottom */);
|
}
|
|
void removeReplacedWindows() {
|
if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
|
mWmService.openSurfaceTransaction();
|
try {
|
forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
|
} finally {
|
mWmService.closeSurfaceTransaction("removeReplacedWindows");
|
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
|
}
|
}
|
|
boolean hasPendingLayoutChanges(WindowAnimator animator) {
|
boolean hasChanges = false;
|
|
final int count = mChildren.size();
|
for (int i = 0; i < count; ++i) {
|
final DisplayContent dc = mChildren.get(i);
|
final int pendingChanges = animator.getPendingLayoutChanges(dc.getDisplayId());
|
if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
|
animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
|
}
|
if (pendingChanges != 0) {
|
hasChanges = true;
|
}
|
}
|
|
return hasChanges;
|
}
|
|
boolean reclaimSomeSurfaceMemory(WindowStateAnimator winAnimator, String operation,
|
boolean secure) {
|
final WindowSurfaceController surfaceController = winAnimator.mSurfaceController;
|
boolean leakedSurface = false;
|
boolean killedApps = false;
|
|
EventLog.writeEvent(EventLogTags.WM_NO_SURFACE_MEMORY, winAnimator.mWin.toString(),
|
winAnimator.mSession.mPid, operation);
|
|
final 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_WM, "Out of memory for surface! Looking for leaks...");
|
final int numDisplays = mChildren.size();
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
leakedSurface |= mChildren.get(displayNdx).destroyLeakedSurfaces();
|
}
|
|
if (!leakedSurface) {
|
Slog.w(TAG_WM, "No leaked surfaces; killing applications!");
|
final SparseIntArray pidCandidates = new SparseIntArray();
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
mChildren.get(displayNdx).forAllWindows((w) -> {
|
if (mWmService.mForceRemoves.contains(w)) {
|
return;
|
}
|
final WindowStateAnimator wsa = w.mWinAnimator;
|
if (wsa.mSurfaceController != null) {
|
pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
|
}
|
}, false /* traverseTopToBottom */);
|
|
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 (mWmService.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_WM,
|
"Looks like we have reclaimed some memory, clearing surface for retry.");
|
if (surfaceController != null) {
|
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
|
"RECOVER DESTROY", false);
|
winAnimator.destroySurface();
|
if (winAnimator.mWin.mAppToken != null) {
|
winAnimator.mWin.mAppToken.removeStartingWindow();
|
}
|
}
|
|
try {
|
winAnimator.mWin.mClient.dispatchGetNewSurface();
|
} catch (RemoteException e) {
|
}
|
}
|
} finally {
|
Binder.restoreCallingIdentity(callingIdentity);
|
}
|
|
return leakedSurface || killedApps;
|
}
|
|
void performSurfacePlacement(boolean recoveringMemory) {
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
|
try {
|
performSurfacePlacementNoTrace(recoveringMemory);
|
} finally {
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
}
|
}
|
|
// "Something has changed! Let's make it correct now."
|
// TODO: Super crazy long method that should be broken down...
|
void performSurfacePlacementNoTrace(boolean recoveringMemory) {
|
if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
|
+ Debug.getCallers(3));
|
|
int i;
|
|
if (mWmService.mFocusMayChange) {
|
mWmService.mFocusMayChange = false;
|
mWmService.updateFocusedWindowLocked(
|
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
|
}
|
|
// Initialize state of exiting tokens.
|
final int numDisplays = mChildren.size();
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
displayContent.setExitingTokensHasVisible(false);
|
}
|
|
mHoldScreen = null;
|
mScreenBrightness = -1;
|
mUserActivityTimeout = -1;
|
mObscureApplicationContentOnSecondaryDisplays = false;
|
mSustainedPerformanceModeCurrent = false;
|
mWmService.mTransactionSequence++;
|
|
// TODO(multi-display): recents animation & wallpaper need support multi-display.
|
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
|
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
|
|
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
|
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
|
mWmService.openSurfaceTransaction();
|
try {
|
applySurfaceChangesTransaction(recoveringMemory);
|
} catch (RuntimeException e) {
|
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
|
} finally {
|
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
|
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
|
}
|
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
|
|
checkAppTransitionReady(surfacePlacer);
|
|
// Defer starting the recents animation until the wallpaper has drawn
|
final RecentsAnimationController recentsAnimationController =
|
mWmService.getRecentsAnimationController();
|
if (recentsAnimationController != null) {
|
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
|
}
|
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
if (displayContent.mWallpaperMayChange) {
|
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
|
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
|
if (DEBUG_LAYOUT_REPEATS) {
|
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
|
displayContent.pendingLayoutChanges);
|
}
|
}
|
}
|
|
if (mWmService.mFocusMayChange) {
|
mWmService.mFocusMayChange = false;
|
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
|
false /*updateInputWindows*/);
|
}
|
|
if (isLayoutNeeded()) {
|
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
|
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("mLayoutNeeded",
|
defaultDisplay.pendingLayoutChanges);
|
}
|
|
handleResizingWindows();
|
|
if (DEBUG_ORIENTATION && mWmService.mDisplayFrozen) Slog.v(TAG,
|
"With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
|
if (mOrientationChangeComplete) {
|
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
|
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
|
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
|
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
|
}
|
mWmService.stopFreezingDisplayLocked();
|
}
|
|
// Destroy the surface of any windows that are no longer visible.
|
i = mWmService.mDestroySurface.size();
|
if (i > 0) {
|
do {
|
i--;
|
WindowState win = mWmService.mDestroySurface.get(i);
|
win.mDestroying = false;
|
final DisplayContent displayContent = win.getDisplayContent();
|
if (displayContent.mInputMethodWindow == win) {
|
displayContent.setInputMethodWindowLocked(null);
|
}
|
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
|
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
|
}
|
win.destroySurfaceUnchecked();
|
win.mWinAnimator.destroyPreservedSurfaceLocked();
|
} while (i > 0);
|
mWmService.mDestroySurface.clear();
|
}
|
|
// Time to remove any exiting tokens?
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
displayContent.removeExistingTokensIfPossible();
|
}
|
|
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
if (displayContent.pendingLayoutChanges != 0) {
|
displayContent.setLayoutNeeded();
|
}
|
}
|
|
mWmService.setHoldScreenLocked(mHoldScreen);
|
if (!mWmService.mDisplayFrozen) {
|
final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
|
? -1 : toBrightnessOverride(mScreenBrightness);
|
|
// Post these on a handler such that we don't call into power manager service while
|
// holding the window manager lock to avoid lock contention with power manager lock.
|
mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightness, 0).sendToTarget();
|
mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
|
}
|
|
if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
|
mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
|
mWmService.mPowerManagerInternal.powerHint(
|
PowerHint.SUSTAINED_PERFORMANCE,
|
(mSustainedPerformanceModeEnabled ? 1 : 0));
|
}
|
|
if (mUpdateRotation) {
|
if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
|
mUpdateRotation = updateRotationUnchecked();
|
}
|
|
if (mWmService.mWaitingForDrawnCallback != null
|
|| (mOrientationChangeComplete && !isLayoutNeeded()
|
&& !mUpdateRotation)) {
|
mWmService.checkDrawnWindowsLocked();
|
}
|
|
final int N = mWmService.mPendingRemove.size();
|
if (N > 0) {
|
if (mWmService.mPendingRemoveTmp.length < N) {
|
mWmService.mPendingRemoveTmp = new WindowState[N + 10];
|
}
|
mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
|
mWmService.mPendingRemove.clear();
|
ArrayList<DisplayContent> displayList = new ArrayList();
|
for (i = 0; i < N; i++) {
|
final WindowState w = mWmService.mPendingRemoveTmp[i];
|
w.removeImmediately();
|
final DisplayContent displayContent = w.getDisplayContent();
|
if (displayContent != null && !displayList.contains(displayContent)) {
|
displayList.add(displayContent);
|
}
|
}
|
|
for (int j = displayList.size() - 1; j >= 0; --j) {
|
final DisplayContent dc = displayList.get(j);
|
dc.assignWindowLayers(true /*setLayoutNeeded*/);
|
}
|
}
|
|
// Remove all deferred displays stacks, tasks, and activities.
|
for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
|
mChildren.get(displayNdx).checkCompleteDeferredRemoval();
|
}
|
|
forAllDisplays(dc -> {
|
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
|
dc.updateSystemGestureExclusion();
|
dc.updateTouchExcludeRegion();
|
});
|
|
// Check to see if we are now in a state where the screen should
|
// be enabled, because the window obscured flags have changed.
|
mWmService.enableScreenIfNeededLocked();
|
|
mWmService.scheduleAnimationLocked();
|
|
if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
|
"performSurfacePlacementInner exit: animating="
|
+ mWmService.mAnimator.isAnimating());
|
}
|
|
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
|
// Trace all displays app transition by Z-order for pending layout change.
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent curDisplay = mChildren.get(i);
|
|
// 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 (curDisplay.mAppTransition.isReady()) {
|
// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
|
curDisplay.mAppTransitionController.handleAppTransitionReady();
|
if (DEBUG_LAYOUT_REPEATS) {
|
surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
|
curDisplay.pendingLayoutChanges);
|
}
|
}
|
|
if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppAnimating()) {
|
// 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!
|
curDisplay.handleAnimatingStoppedAndTransition();
|
if (DEBUG_LAYOUT_REPEATS) {
|
surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
|
curDisplay.pendingLayoutChanges);
|
}
|
}
|
}
|
}
|
|
private void applySurfaceChangesTransaction(boolean recoveringMemory) {
|
mHoldScreenWindow = null;
|
mObscuringWindow = null;
|
|
// TODO(multi-display): Support these features on secondary screens.
|
final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
|
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
|
final int defaultDw = defaultInfo.logicalWidth;
|
final int defaultDh = defaultInfo.logicalHeight;
|
if (mWmService.mWatermark != null) {
|
mWmService.mWatermark.positionSurface(defaultDw, defaultDh);
|
}
|
if (mWmService.mStrictModeFlash != null) {
|
mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
|
}
|
if (mWmService.mCircularDisplayMask != null) {
|
mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
|
mWmService.getDefaultDisplayRotation());
|
}
|
if (mWmService.mEmulatorDisplayOverlay != null) {
|
mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
|
mWmService.getDefaultDisplayRotation());
|
}
|
|
final int count = mChildren.size();
|
for (int j = 0; j < count; ++j) {
|
final DisplayContent dc = mChildren.get(j);
|
dc.applySurfaceChangesTransaction(recoveringMemory);
|
}
|
|
// Give the display manager a chance to adjust properties like display rotation if it needs
|
// to.
|
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
|
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
|
}
|
|
/**
|
* Handles resizing windows during surface placement.
|
*/
|
private void handleResizingWindows() {
|
for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
|
WindowState win = mWmService.mResizingWindows.get(i);
|
if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) {
|
// Don't remove this window until rotation has completed and is not waiting for the
|
// complete configuration.
|
continue;
|
}
|
win.reportResized();
|
mWmService.mResizingWindows.remove(i);
|
}
|
}
|
|
/**
|
* @param w WindowState this method is applied to.
|
* @param obscured True if there is a window on top of this obscuring the display.
|
* @param syswin System window?
|
* @return True when the display contains content to show the user. When false, the display
|
* manager may choose to mirror or blank the display.
|
*/
|
boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
|
final WindowManager.LayoutParams attrs = w.mAttrs;
|
final int attrFlags = attrs.flags;
|
final boolean onScreen = w.isOnScreen();
|
final boolean canBeSeen = w.isDisplayedLw();
|
final int privateflags = attrs.privateFlags;
|
boolean displayHasContent = false;
|
|
if (DEBUG_KEEP_SCREEN_ON) {
|
Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked w: " + w
|
+ ", w.mHasSurface: " + w.mHasSurface
|
+ ", w.isOnScreen(): " + onScreen
|
+ ", w.isDisplayedLw(): " + w.isDisplayedLw()
|
+ ", w.mAttrs.userActivityTimeout: " + w.mAttrs.userActivityTimeout);
|
}
|
if (w.mHasSurface && onScreen) {
|
if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
|
mUserActivityTimeout = w.mAttrs.userActivityTimeout;
|
if (DEBUG_KEEP_SCREEN_ON) {
|
Slog.d(TAG, "mUserActivityTimeout set to " + mUserActivityTimeout);
|
}
|
}
|
}
|
if (w.mHasSurface && canBeSeen) {
|
if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
|
mHoldScreen = w.mSession;
|
mHoldScreenWindow = w;
|
} else if (DEBUG_KEEP_SCREEN_ON && w == mWmService.mLastWakeLockHoldingWindow) {
|
Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding "
|
+ "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
|
+ Debug.getCallers(10));
|
}
|
if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
|
mScreenBrightness = w.mAttrs.screenBrightness;
|
}
|
|
final int type = attrs.type;
|
// This function assumes that the contents of the default display are processed first
|
// before secondary displays.
|
final DisplayContent displayContent = w.getDisplayContent();
|
if (displayContent != null && displayContent.isDefaultDisplay) {
|
// While a dream or keyguard is showing, obscure ordinary application content on
|
// secondary displays (by forcibly enabling mirroring unless there is other content
|
// we want to show) but still allow opaque keyguard dialogs to be shown.
|
if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
|
mObscureApplicationContentOnSecondaryDisplays = true;
|
}
|
displayHasContent = true;
|
} else if (displayContent != null &&
|
(!mObscureApplicationContentOnSecondaryDisplays
|
|| (obscured && type == TYPE_KEYGUARD_DIALOG))) {
|
// Allow full screen keyguard presentation dialogs to be seen.
|
displayHasContent = true;
|
}
|
if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
|
mSustainedPerformanceModeCurrent = true;
|
}
|
}
|
|
return displayHasContent;
|
}
|
|
boolean updateRotationUnchecked() {
|
boolean changed = false;
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final DisplayContent displayContent = mChildren.get(i);
|
if (displayContent.updateRotationAndSendNewConfigIfNeeded()) {
|
changed = true;
|
}
|
}
|
return changed;
|
}
|
|
boolean copyAnimToLayoutParams() {
|
boolean doRequest = false;
|
|
final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
|
if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
|
mUpdateRotation = true;
|
doRequest = true;
|
}
|
if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
|
mOrientationChangeComplete = false;
|
} else {
|
mOrientationChangeComplete = true;
|
mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
|
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
|
doRequest = true;
|
}
|
}
|
|
if ((bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING) != 0) {
|
mWallpaperActionPending = true;
|
}
|
|
return doRequest;
|
}
|
|
private static int toBrightnessOverride(float value) {
|
return (int)(value * PowerManager.BRIGHTNESS_ON);
|
}
|
|
private final class MyHandler extends Handler {
|
|
public MyHandler(Looper looper) {
|
super(looper);
|
}
|
|
@Override
|
public void handleMessage(Message msg) {
|
switch (msg.what) {
|
case SET_SCREEN_BRIGHTNESS_OVERRIDE:
|
mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
|
msg.arg1);
|
break;
|
case SET_USER_ACTIVITY_TIMEOUT:
|
mWmService.mPowerManagerInternal.
|
setUserActivityTimeoutOverrideFromWindowManager((Long) msg.obj);
|
break;
|
default:
|
break;
|
}
|
}
|
}
|
|
void dumpDisplayContents(PrintWriter pw) {
|
pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)");
|
if (mWmService.mDisplayReady) {
|
final int count = mChildren.size();
|
for (int i = 0; i < count; ++i) {
|
final DisplayContent displayContent = mChildren.get(i);
|
displayContent.dump(pw, " ", true /* dumpAll */);
|
}
|
} else {
|
pw.println(" NO DISPLAY");
|
}
|
}
|
|
void dumpTopFocusedDisplayId(PrintWriter pw) {
|
pw.print(" mTopFocusedDisplayId="); pw.println(mTopFocusedDisplayId);
|
}
|
|
void dumpLayoutNeededDisplayIds(PrintWriter pw) {
|
if (!isLayoutNeeded()) {
|
return;
|
}
|
pw.print(" mLayoutNeeded on displays=");
|
final int count = mChildren.size();
|
for (int displayNdx = 0; displayNdx < count; ++displayNdx) {
|
final DisplayContent displayContent = mChildren.get(displayNdx);
|
if (displayContent.isLayoutNeeded()) {
|
pw.print(displayContent.getDisplayId());
|
}
|
}
|
pw.println();
|
}
|
|
void dumpWindowsNoHeader(PrintWriter pw, boolean dumpAll, ArrayList<WindowState> windows) {
|
final int[] index = new int[1];
|
forAllWindows((w) -> {
|
if (windows == null || windows.contains(w)) {
|
pw.println(" Window #" + index[0] + " " + w + ":");
|
w.dump(pw, " ", dumpAll || windows != null);
|
index[0] = index[0] + 1;
|
}
|
}, true /* traverseTopToBottom */);
|
}
|
|
void dumpTokens(PrintWriter pw, boolean dumpAll) {
|
pw.println(" All tokens:");
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
mChildren.get(i).dumpTokens(pw, dumpAll);
|
}
|
}
|
|
@CallSuper
|
@Override
|
public void writeToProto(ProtoOutputStream proto, long fieldId,
|
@WindowTraceLogLevel int logLevel) {
|
if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
|
return;
|
}
|
|
final long token = proto.start(fieldId);
|
super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
|
if (mWmService.mDisplayReady) {
|
final int count = mChildren.size();
|
for (int i = 0; i < count; ++i) {
|
final DisplayContent displayContent = mChildren.get(i);
|
displayContent.writeToProto(proto, DISPLAYS, logLevel);
|
}
|
}
|
if (logLevel == WindowTraceLogLevel.ALL) {
|
forAllWindows((w) -> {
|
w.writeIdentifierToProto(proto, WINDOWS);
|
}, true);
|
}
|
proto.end(token);
|
}
|
|
@Override
|
String getName() {
|
return "ROOT";
|
}
|
|
@Override
|
void positionChildAt(int position, DisplayContent child, boolean includingParents) {
|
super.positionChildAt(position, child, includingParents);
|
if (mRootActivityContainer != null) {
|
mRootActivityContainer.onChildPositionChanged(child.mAcitvityDisplay, position);
|
}
|
}
|
|
void positionChildAt(int position, DisplayContent child) {
|
// Only called from controller so no need to notify the change to controller.
|
super.positionChildAt(position, child, false /* includingParents */);
|
}
|
|
@Override
|
void scheduleAnimation() {
|
mWmService.scheduleAnimationLocked();
|
}
|
|
@Override
|
protected void removeChild(DisplayContent dc) {
|
super.removeChild(dc);
|
if (mTopFocusedDisplayId == dc.getDisplayId()) {
|
mWmService.updateFocusedWindowLocked(
|
UPDATE_FOCUS_NORMAL, true /* updateInputWindows */);
|
}
|
}
|
|
/**
|
* For all display at or below this call the callback.
|
*
|
* @param callback Callback to be called for every display.
|
*/
|
void forAllDisplays(Consumer<DisplayContent> callback) {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
callback.accept(mChildren.get(i));
|
}
|
}
|
|
void forAllDisplayPolicies(Consumer<DisplayPolicy> callback) {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
callback.accept(mChildren.get(i).getDisplayPolicy());
|
}
|
}
|
|
/**
|
* Get current topmost focused IME window in system.
|
* Will look on all displays in current Z-order.
|
*/
|
WindowState getCurrentInputMethodWindow() {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
final DisplayContent displayContent = mChildren.get(i);
|
if (displayContent.mInputMethodWindow != null) {
|
return displayContent.mInputMethodWindow;
|
}
|
}
|
return null;
|
}
|
}
|