blob: 4e4398ee9d91e0a774a1720761e2f0f8d081c223 [file] [log] [blame]
/*
* 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager.TaskSnapshot;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Trace;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IApplicationToken;
import android.view.WindowManagerPolicy.StartingSurface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.AttributeCache;
/**
* Controller for the app window token container. This is created by activity manager to link
* activity records to the app window token container they use in window manager.
*
* Test class: {@link AppWindowContainerControllerTests}
*/
public class AppWindowContainerController
extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
private static final int STARTING_WINDOW_TYPE_NONE = 0;
private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
private final IApplicationToken mToken;
private final Handler mHandler;
private final class H extends Handler {
public static final int NOTIFY_WINDOWS_DRAWN = 1;
public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
public H(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NOTIFY_WINDOWS_DRAWN:
if (mListener == null) {
return;
}
if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ AppWindowContainerController.this.mToken);
mListener.onWindowsDrawn(msg.getWhen());
break;
case NOTIFY_STARTING_WINDOW_DRAWN:
if (mListener == null) {
return;
}
if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ AppWindowContainerController.this.mToken);
mListener.onStartingWindowDrawn(msg.getWhen());
break;
default:
break;
}
}
}
private final Runnable mOnWindowsVisible = () -> {
if (mListener == null) {
return;
}
if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
+ AppWindowContainerController.this.mToken);
mListener.onWindowsVisible();
};
private final Runnable mOnWindowsGone = () -> {
if (mListener == null) {
return;
}
if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
+ AppWindowContainerController.this.mToken);
mListener.onWindowsGone();
};
private final Runnable mRemoveStartingWindow = () -> {
StartingSurface surface = null;
synchronized (mWindowMap) {
if (mContainer == null) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
+ " remove starting window");
return;
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + mContainer
+ ": startingWindow=" + mContainer.startingWindow
+ " startingView=" + mContainer.startingSurface);
if (mContainer.startingData != null) {
surface = mContainer.startingSurface;
mContainer.startingData = null;
mContainer.startingSurface = null;
mContainer.startingWindow = null;
mContainer.startingDisplayed = false;
if (surface == null && DEBUG_STARTING_WINDOW) {
Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
+ "remove");
}
} else if (DEBUG_STARTING_WINDOW) {
Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
+ mContainer);
}
}
if (surface != null) {
try {
surface.remove();
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when removing starting window", e);
}
}
};
private final Runnable mAddStartingWindow = () -> {
final StartingData startingData;
final AppWindowToken container;
synchronized (mWindowMap) {
if (mContainer == null) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
+ " add starting window");
return;
}
startingData = mContainer.startingData;
container = mContainer;
}
if (startingData == null) {
// Animation has been canceled... do nothing.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "startingData was nulled out before handling"
+ " mAddStartingWindow: " + mContainer);
return;
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
+ this + ": startingData=" + container.startingData);
StartingSurface surface = null;
try {
surface = startingData.createStartingSurface(container);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when adding starting window", e);
}
if (surface != null) {
boolean abort = false;
synchronized(mWindowMap) {
// If the window was successfully added, then
// we need to remove it.
if (container.removed || container.startingData == null) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
"Aborted starting " + container
+ ": removed=" + container.removed
+ " startingData=" + container.startingData);
container.startingWindow = null;
container.startingData = null;
abort = true;
} else {
container.startingSurface = surface;
}
if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
"Added starting " + mContainer
+ ": startingWindow="
+ container.startingWindow + " startingView="
+ container.startingSurface);
}
if (abort) {
surface.remove();
}
} else if (DEBUG_STARTING_WINDOW) {
Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
}
};
public AppWindowContainerController(TaskWindowContainerController taskController,
IApplicationToken token, AppWindowContainerListener listener, int index,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
Configuration overrideConfig, Rect bounds) {
this(taskController, token, listener, index, requestedOrientation, fullscreen,
showForAllUsers,
configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable,
targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos,
WindowManagerService.getInstance(), overrideConfig, bounds);
}
public AppWindowContainerController(TaskWindowContainerController taskController,
IApplicationToken token, AppWindowContainerListener listener, int index,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
WindowManagerService service, Configuration overrideConfig, Rect bounds) {
super(listener, service);
mHandler = new H(service.mH.getLooper());
mToken = token;
synchronized(mWindowMap) {
AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
if (atoken != null) {
// TODO: Should this throw an exception instead?
Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
return;
}
final Task task = taskController.mContainer;
if (task == null) {
throw new IllegalArgumentException("AppWindowContainerController: invalid "
+ " controller=" + taskController);
}
atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
alwaysFocusable, this, overrideConfig, bounds);
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
+ " controller=" + taskController + " at " + index);
task.addChild(atoken, index);
}
}
@VisibleForTesting
AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
boolean alwaysFocusable, AppWindowContainerController controller,
Configuration overrideConfig, Rect bounds) {
return new AppWindowToken(service, token, voiceInteraction, dc,
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
controller, overrideConfig, bounds);
}
public void removeContainer(int displayId) {
synchronized(mWindowMap) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
+ mToken + " from non-existing displayId=" + displayId);
return;
}
dc.removeAppToken(mToken.asBinder());
super.removeContainer();
}
}
@Override
public void removeContainer() {
throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
}
public void reparent(TaskWindowContainerController taskController, int position) {
synchronized (mWindowMap) {
if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
+ " to task=" + taskController + " at " + position);
if (mContainer == null) {
if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
"reparent: could not find app token=" + mToken);
return;
}
final Task task = taskController.mContainer;
if (task == null) {
throw new IllegalArgumentException("reparent: could not find task="
+ taskController);
}
mContainer.reparent(task, position);
mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
}
}
public Configuration setOrientation(int requestedOrientation, int displayId,
Configuration displayConfig, boolean freezeScreenIfNeeded) {
synchronized(mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM,
"Attempted to set orientation of non-existing app token: " + mToken);
return null;
}
mContainer.setOrientation(requestedOrientation);
final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
}
}
public int getOrientation() {
synchronized(mWindowMap) {
if (mContainer == null) {
return SCREEN_ORIENTATION_UNSPECIFIED;
}
return mContainer.getOrientationIgnoreVisibility();
}
}
// TODO(b/36505427): Maybe move to WindowContainerController so other sub-classes can use it as
// a generic way to set override config. Need to untangle current ways the override config is
// currently set for tasks and displays before we are doing that though.
public void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) {
synchronized(mWindowMap) {
if (mContainer != null) {
mContainer.onOverrideConfigurationChanged(overrideConfiguration, bounds);
}
}
}
public void setDisablePreviewScreenshots(boolean disable) {
synchronized (mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
+ " token: " + mToken);
return;
}
mContainer.setDisablePreviewScreenshots(disable);
}
}
public void setVisibility(boolean visible, boolean deferHidingClient) {
synchronized(mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+ mToken);
return;
}
final AppWindowToken wtoken = mContainer;
// Don't set visibility to false if we were already not visible. This prevents WM from
// adding the app to the closing app list which doesn't make sense for something that is
// already not visible. However, set visibility to true even if we are already visible.
// This makes sure the app is added to the opening apps list so that the right
// transition can be selected.
// TODO: Probably a good idea to separate the concept of opening/closing apps from the
// concept of setting visibility...
if (!visible && wtoken.hiddenRequested) {
if (!deferHidingClient && wtoken.mDeferHidingClient) {
// We previously deferred telling the client to hide itself when visibility was
// initially set to false. Now we would like it to hide, so go ahead and set it.
wtoken.mDeferHidingClient = deferHidingClient;
wtoken.setClientHidden(true);
}
return;
}
if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
+ mToken + ", visible=" + visible + "): " + mService.mAppTransition
+ " hidden=" + wtoken.hidden + " hiddenRequested="
+ wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
mService.mOpeningApps.remove(wtoken);
mService.mClosingApps.remove(wtoken);
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
wtoken.mDeferHidingClient = deferHidingClient;
if (!visible) {
// If the app is dead while it was visible, we kept its dead window on screen.
// Now that the app is going invisible, we can remove it. It will be restarted
// if made visible again.
wtoken.removeDeadWindows();
wtoken.setVisibleBeforeClientHidden();
mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
} else {
if (!mService.mAppTransition.isTransitionSet()
&& mService.mAppTransition.isReady()) {
// Add the app mOpeningApps if transition is unset but ready. This means
// we're doing a screen freeze, and the unfreeze will wait for all opening
// apps to be ready.
mService.mOpeningApps.add(wtoken);
}
wtoken.startingMoved = false;
// If the token is currently hidden (should be the common case), or has been
// stopped, then we need to set up to wait for its windows to be ready.
if (wtoken.hidden || wtoken.mAppStopped) {
wtoken.clearAllDrawn();
// If the app was already visible, don't reset the waitingToShow state.
if (wtoken.hidden) {
wtoken.waitingToShow = true;
}
if (wtoken.isClientHidden()) {
// 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.setClientHidden(false);
}
}
wtoken.requestUpdateWallpaperIfNeeded();
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false;
}
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
if (mService.okToDisplay() && mService.mAppTransition.isTransitionSet()) {
// A dummy animation is a placeholder animation which informs others that an
// animation is going on (in this case an application transition). If the animation
// was transferred from another application/animator, no dummy animator should be
// created since an animation is already in progress.
if (wtoken.mAppAnimator.usingTransferredAnimation
&& wtoken.mAppAnimator.animation == null) {
Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken
+ ", using null transferred animation!");
}
if (!wtoken.mAppAnimator.usingTransferredAnimation &&
(!wtoken.startingDisplayed || mService.mSkipAppTransitionAnimation)) {
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG_WM, "Setting dummy animation on: " + wtoken);
wtoken.mAppAnimator.setDummyAnimation();
}
wtoken.inPendingTransaction = true;
if (visible) {
mService.mOpeningApps.add(wtoken);
wtoken.mEnteringAnimation = true;
} else {
mService.mClosingApps.add(wtoken);
wtoken.mEnteringAnimation = false;
}
if (mService.mAppTransition.getAppTransition()
== AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
final WindowState win =
mService.getDefaultDisplayContentLocked().findFocusedWindow();
if (win != null) {
final AppWindowToken focusedToken = win.mAppToken;
if (focusedToken != null) {
if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
+ " adding " + focusedToken + " to mOpeningApps");
// Force animation to be loaded.
focusedToken.hidden = true;
mService.mOpeningApps.add(focusedToken);
}
}
}
return;
}
wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
wtoken.updateReportedVisibilityLocked();
}
}
/**
* Notifies that we launched an app that might be visible or not visible depending on what kind
* of Keyguard flags it's going to set on its windows.
*/
public void notifyUnknownVisibilityLaunched() {
synchronized(mWindowMap) {
if (mContainer != null) {
mService.mUnknownAppVisibilityController.notifyLaunched(mContainer);
}
}
}
public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
synchronized(mWindowMap) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
+ " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
+ " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
+ " allowTaskSnapshot=" + allowTaskSnapshot);
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
return false;
}
// 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 (!mService.okToDisplay()) {
return false;
}
if (mContainer.startingData != null) {
return false;
}
final WindowState mainWin = mContainer.findMainWindow();
if (mainWin != null && mainWin.isVisible() && mainWin.isDrawnLw()) {
// App already has a visible window that is drawn...why would you want a starting
// window?
return false;
}
final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
false /* restoreFromDisk */, false /* reducedResolution */);
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
allowTaskSnapshot, activityCreated, fromRecents, snapshot);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
return createSnapshot(snapshot);
}
// 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_WM, "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, mService.mCurrentUserId);
if (ent == null) {
// Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
// see that.
return false;
}
final boolean windowIsTranslucent = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
final boolean windowIsFloating = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsFloating, false);
final boolean windowShowWallpaper = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowShowWallpaper, false);
final boolean windowDisableStarting = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowDisablePreview, false);
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
+ " Floating=" + windowIsFloating
+ " ShowWallpaper=" + windowShowWallpaper);
if (windowIsTranslucent) {
return false;
}
if (windowIsFloating || windowDisableStarting) {
return false;
}
if (windowShowWallpaper) {
if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
== null) {
// If this theme is requesting a wallpaper, and the wallpaper
// is not currently 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 false;
}
}
}
if (mContainer.transferStartingWindow(transferFrom)) {
return true;
}
// There is no existing starting window, and we don't want to create a splash screen, so
// that's it!
if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
return false;
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
mContainer.getMergedOverrideConfiguration());
scheduleAddStartingWindow();
}
return true;
}
private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
TaskSnapshot snapshot) {
if (mService.mAppTransition.getAppTransition() == TRANSIT_DOCK_TASK_FROM_RECENTS) {
// TODO(b/34099271): Remove this statement to add back the starting window and figure
// out why it causes flickering, the starting window appears over the thumbnail while
// the docked from recents transition occurs
return STARTING_WINDOW_TYPE_NONE;
} else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
return snapshot == null ? STARTING_WINDOW_TYPE_NONE
: snapshotOrientationSameAsTask(snapshot) || fromRecents
? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else {
return STARTING_WINDOW_TYPE_NONE;
}
}
void scheduleAddStartingWindow() {
// 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_WM, "Enqueueing ADD_STARTING");
mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
}
private boolean createSnapshot(TaskSnapshot snapshot) {
if (snapshot == null) {
return false;
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
mContainer.startingData = new SnapshotStartingData(mService, snapshot);
scheduleAddStartingWindow();
return true;
}
private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
if (snapshot == null) {
return false;
}
return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
}
public void removeStartingWindow() {
synchronized (mWindowMap) {
if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
// Already scheduled.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Trying to remove starting window but "
+ "already scheduled");
return;
}
if (mContainer.startingWindow == null) {
if (mContainer.startingData != null) {
// Starting window has not been added yet, but it is scheduled to be added.
// Go ahead and cancel the request.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
"Clearing startingData for token=" + mContainer);
mContainer.startingData = null;
}
return;
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
+ " startingWindow=" + mContainer.startingWindow);
mHandler.post(mRemoveStartingWindow);
}
}
public void pauseKeyDispatching() {
synchronized (mWindowMap) {
if (mContainer != null) {
mService.mInputMonitor.pauseDispatchingLw(mContainer);
}
}
}
public void resumeKeyDispatching() {
synchronized (mWindowMap) {
if (mContainer != null) {
mService.mInputMonitor.resumeDispatchingLw(mContainer);
}
}
}
public void notifyAppResumed(boolean wasStopped) {
synchronized(mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
return;
}
mContainer.notifyAppResumed(wasStopped);
}
}
public void notifyAppStopped() {
synchronized(mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
+ mToken);
return;
}
mContainer.notifyAppStopped();
}
}
public void startFreezingScreen(int configChanges) {
synchronized(mWindowMap) {
if (configChanges == 0 && mService.okToDisplay()) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
return;
}
if (mContainer == null) {
Slog.w(TAG_WM,
"Attempted to freeze screen with non-existing app token: " + mContainer);
return;
}
mContainer.startFreezingScreen();
}
}
public void stopFreezingScreen(boolean force) {
synchronized(mWindowMap) {
if (mContainer == null) {
return;
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
+ mContainer.hidden + " freezing=" + mContainer.mAppAnimator.freezingScreen);
mContainer.stopFreezingScreen(true, force);
}
}
/**
* Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
* In portrait mode, it grabs the full screenshot.
*
* @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 frameScale the scale to apply to the frame, only used when width = -1 and height = -1
*/
public Bitmap screenshotApplications(int displayId, int width, int height, float frameScale) {
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
final DisplayContent dc;
synchronized(mWindowMap) {
dc = mRoot.getDisplayContentOrCreate(displayId);
if (dc == null) {
if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + mToken
+ ": returning null. No Display for displayId=" + displayId);
return null;
}
}
return dc.screenshotApplications(mToken.asBinder(), width, height,
false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
false /* wallpaperOnly */, false /* includeDecor */);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
void reportStartingWindowDrawn() {
mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
}
void reportWindowsDrawn() {
mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
}
void reportWindowsVisible() {
mHandler.post(mOnWindowsVisible);
}
void reportWindowsGone() {
mHandler.post(mOnWindowsGone);
}
/** Calls directly into activity manager so window manager lock shouldn't held. */
boolean keyDispatchingTimedOut(String reason, int windowPid) {
return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
}
@Override
public String toString() {
return "AppWindowContainerController{"
+ " token=" + mToken
+ " mContainer=" + mContainer
+ " mListener=" + mListener
+ "}";
}
}