Merge "Cleanup old SplashScreen code"
This commit is contained in:
@@ -21,15 +21,12 @@ import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
|
||||
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
|
||||
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
|
||||
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
|
||||
import static android.content.Context.CONTEXT_RESTRICTED;
|
||||
import static android.content.Context.WINDOW_SERVICE;
|
||||
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
|
||||
import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
|
||||
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
|
||||
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
|
||||
import static android.content.pm.PackageManager.FEATURE_WATCH;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.content.res.Configuration.EMPTY;
|
||||
import static android.os.Build.VERSION_CODES.M;
|
||||
import static android.os.Build.VERSION_CODES.O;
|
||||
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
|
||||
@@ -47,7 +44,6 @@ import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
|
||||
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
|
||||
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
|
||||
@@ -117,13 +113,10 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.hdmi.HdmiAudioSystemClient;
|
||||
@@ -182,7 +175,6 @@ import android.view.KeyCharacterMap;
|
||||
import android.view.KeyCharacterMap.FallbackAction;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
@@ -249,9 +241,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
static final boolean localLOGV = false;
|
||||
static final boolean DEBUG_INPUT = false;
|
||||
static final boolean DEBUG_KEYGUARD = false;
|
||||
static final boolean DEBUG_SPLASH_SCREEN = false;
|
||||
static final boolean DEBUG_WAKEUP = false;
|
||||
static final boolean SHOW_SPLASH_SCREENS = true;
|
||||
|
||||
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
|
||||
// No longer recommended for desk docks;
|
||||
@@ -2354,7 +2344,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
|
||||
if (!mContext.getResources()
|
||||
.getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
|
||||
mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
|
||||
mRingerToggleChord = VOLUME_HUSH_OFF;
|
||||
}
|
||||
|
||||
// Configure wake gesture.
|
||||
@@ -2572,191 +2562,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
return attrs.type == TYPE_NOTIFICATION_SHADE;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
|
||||
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
|
||||
int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
|
||||
if (!SHOW_SPLASH_SCREENS) {
|
||||
return null;
|
||||
}
|
||||
if (packageName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
WindowManager wm = null;
|
||||
View view = null;
|
||||
|
||||
try {
|
||||
Context context = mContext;
|
||||
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName
|
||||
+ ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
|
||||
+ Integer.toHexString(theme));
|
||||
|
||||
// Obtain proper context to launch on the right display.
|
||||
final Context displayContext = getDisplayContext(context, displayId);
|
||||
if (displayContext == null) {
|
||||
// Can't show splash screen on requested display, so skip showing at all.
|
||||
return null;
|
||||
}
|
||||
context = displayContext;
|
||||
|
||||
if (theme != context.getThemeResId() || labelRes != 0) {
|
||||
try {
|
||||
context = context.createPackageContextAsUser(packageName, CONTEXT_RESTRICTED,
|
||||
UserHandle.of(userId));
|
||||
context.setTheme(theme);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.w(TAG, "Failed creating package context with package name "
|
||||
+ packageName + " for user " + userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {
|
||||
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"
|
||||
+ " on overrideConfig" + overrideConfig + " for splash screen");
|
||||
final Context overrideContext = context.createConfigurationContext(overrideConfig);
|
||||
overrideContext.setTheme(theme);
|
||||
final TypedArray typedArray = overrideContext.obtainStyledAttributes(
|
||||
com.android.internal.R.styleable.Window);
|
||||
final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
|
||||
if (resId != 0 && overrideContext.getDrawable(resId) != null) {
|
||||
// We want to use the windowBackground for the override context if it is
|
||||
// available, otherwise we use the default one to make sure a themed starting
|
||||
// window is displayed for the app.
|
||||
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"
|
||||
+ overrideConfig + " to starting window resId=" + resId);
|
||||
context = overrideContext;
|
||||
}
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
final PhoneWindow win = new PhoneWindow(context);
|
||||
win.setIsStartingWindow(true);
|
||||
|
||||
CharSequence label = context.getResources().getText(labelRes, null);
|
||||
// Only change the accessibility title if the label is localized
|
||||
if (label != null) {
|
||||
win.setTitle(label, true);
|
||||
} else {
|
||||
win.setTitle(nonLocalizedLabel, false);
|
||||
}
|
||||
|
||||
win.setType(
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
|
||||
|
||||
synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
|
||||
// Assumes it's safe to show starting windows of launched apps while
|
||||
// the keyguard is being hidden. This is okay because starting windows never show
|
||||
// secret information.
|
||||
// TODO(b/113840485): Occluded may not only happen on default display
|
||||
if (displayId == DEFAULT_DISPLAY && isKeyguardOccluded()) {
|
||||
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
// Force the window flags: this is a fake window, so it is not really
|
||||
// touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
|
||||
// flag because we do know that the next window will take input
|
||||
// focus, so we want to get the IME window up on top of us right away.
|
||||
win.setFlags(
|
||||
windowFlags|
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
|
||||
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
|
||||
windowFlags|
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
|
||||
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
|
||||
|
||||
win.setDefaultIcon(icon);
|
||||
win.setDefaultLogo(logo);
|
||||
|
||||
win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT);
|
||||
|
||||
final WindowManager.LayoutParams params = win.getAttributes();
|
||||
params.token = appToken;
|
||||
params.packageName = packageName;
|
||||
params.windowAnimations = win.getWindowStyle().getResourceId(
|
||||
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
|
||||
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
// Setting as trusted overlay to let touches pass through. This is safe because this
|
||||
// window is controlled by the system.
|
||||
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
|
||||
if (!compatInfo.supportsScreen()) {
|
||||
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
|
||||
}
|
||||
|
||||
params.setTitle("Splash Screen " + packageName);
|
||||
addSplashscreenContent(win, context);
|
||||
|
||||
wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
|
||||
view = win.getDecorView();
|
||||
|
||||
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
|
||||
+ packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
|
||||
|
||||
wm.addView(view, params);
|
||||
|
||||
// Only return the view if it was successfully added to the
|
||||
// window manager... which we can tell by it having a parent.
|
||||
return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
|
||||
} catch (WindowManager.BadTokenException e) {
|
||||
// ignore
|
||||
Log.w(TAG, appToken + " already running, starting window not displayed. " +
|
||||
e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
// don't crash if something else bad happens, for example a
|
||||
// failure loading resources because we are loading from an app
|
||||
// on external storage that has been unmounted.
|
||||
Log.w(TAG, appToken + " failed creating starting window", e);
|
||||
} finally {
|
||||
if (view != null && view.getParent() == null) {
|
||||
Log.w(TAG, "view not successfully added to wm, removing view");
|
||||
wm.removeViewImmediate(view);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addSplashscreenContent(PhoneWindow win, Context ctx) {
|
||||
final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
|
||||
final int resId = a.getResourceId(R.styleable.Window_windowSplashscreenContent, 0);
|
||||
a.recycle();
|
||||
if (resId == 0) {
|
||||
return;
|
||||
}
|
||||
final Drawable drawable = ctx.getDrawable(resId);
|
||||
if (drawable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We wrap this into a view so the system insets get applied to the drawable.
|
||||
final View v = new View(ctx);
|
||||
v.setBackground(drawable);
|
||||
win.setContentView(v);
|
||||
}
|
||||
|
||||
/** Obtain proper context for showing splash screen on the provided display. */
|
||||
private Context getDisplayContext(Context context, int displayId) {
|
||||
if (displayId == DEFAULT_DISPLAY) {
|
||||
// The default context fits.
|
||||
return context;
|
||||
}
|
||||
|
||||
final Display targetDisplay = mDisplayManager.getDisplay(displayId);
|
||||
if (targetDisplay == null) {
|
||||
// Failed to obtain the non-default display where splash screen should be shown,
|
||||
// lets not show at all.
|
||||
return null;
|
||||
}
|
||||
|
||||
return context.createDisplayContext(targetDisplay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animation createHiddenByKeyguardExit(boolean onWallpaper,
|
||||
boolean goingToNotificationShade, boolean subtleAnimation) {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import static com.android.server.policy.PhoneWindowManager.DEBUG_SPLASH_SCREEN;
|
||||
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.util.Slog;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.policy.DecorView;
|
||||
import com.android.internal.policy.PhoneWindow;
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
|
||||
/**
|
||||
* Holds the contents of a splash screen starting window, i.e. the {@link DecorView} of a
|
||||
* {@link PhoneWindow}. This is just a wrapper such that we can return it from
|
||||
* {@link WindowManagerPolicy#addSplashScreen}.
|
||||
*/
|
||||
class SplashScreenSurface implements StartingSurface {
|
||||
|
||||
private static final String TAG = PhoneWindowManager.TAG;
|
||||
private final View mView;
|
||||
private final IBinder mAppToken;
|
||||
|
||||
SplashScreenSurface(View view, IBinder appToken) {
|
||||
mView = view;
|
||||
mAppToken = appToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(boolean animate) {
|
||||
if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": "
|
||||
+ this + " Callers=" + Debug.getCallers(4));
|
||||
|
||||
final WindowManager wm = mView.getContext().getSystemService(WindowManager.class);
|
||||
wm.removeView(mView);
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,6 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
@@ -223,21 +222,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
|
||||
boolean canShowWhenLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the
|
||||
* contents of the starting window into an class implementing this interface, which then will be
|
||||
* held by WM and released with {@link #remove} when no longer needed.
|
||||
*/
|
||||
interface StartingSurface {
|
||||
|
||||
/**
|
||||
* Removes the starting window surface. Do not hold the window manager lock when calling
|
||||
* this method!
|
||||
* @param animate Whether need to play the default exit animation for starting window.
|
||||
*/
|
||||
void remove(boolean animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for calling back in to the window manager that is private
|
||||
* between it and the policy.
|
||||
@@ -692,33 +676,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the system would like to show a UI to indicate that an
|
||||
* application is starting. You can use this to add a
|
||||
* APPLICATION_STARTING_TYPE window with the given appToken to the window
|
||||
* manager (using the normal window manager APIs) that will be shown until
|
||||
* the application displays its own window. This is called without the
|
||||
* window manager locked so that you can call back into it.
|
||||
*
|
||||
* @param appToken Token of the application being started.
|
||||
* @param packageName The name of the application package being started.
|
||||
* @param theme Resource defining the application's overall visual theme.
|
||||
* @param nonLocalizedLabel The default title label of the application if
|
||||
* no data is found in the resource.
|
||||
* @param labelRes The resource ID the application would like to use as its name.
|
||||
* @param icon The resource ID the application would like to use as its icon.
|
||||
* @param windowFlags Window layout flags.
|
||||
* @param overrideConfig override configuration to consider when generating
|
||||
* context to for resources.
|
||||
* @param displayId Id of the display to show the splash screen at.
|
||||
*
|
||||
* @return The starting surface.
|
||||
*
|
||||
*/
|
||||
StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
|
||||
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
|
||||
int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId);
|
||||
|
||||
/**
|
||||
* Create and return an animation to re-display a window that was force hidden by Keyguard.
|
||||
*/
|
||||
|
||||
@@ -103,7 +103,6 @@ import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
import static android.view.SurfaceControl.getGlobalTransaction;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
@@ -333,7 +332,6 @@ import com.android.server.am.AppTimeTracker;
|
||||
import com.android.server.am.PendingIntentRecord;
|
||||
import com.android.server.contentcapture.ContentCaptureManagerInternal;
|
||||
import com.android.server.display.color.ColorDisplayService;
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
import com.android.server.uri.NeededUriGrants;
|
||||
import com.android.server.uri.UriPermissionOwner;
|
||||
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
|
||||
@@ -451,9 +449,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
private CharSequence nonLocalizedLabel; // the label information from the package mgr.
|
||||
private int labelRes; // the label information from the package mgr.
|
||||
private int icon; // resource identifier of activity's icon.
|
||||
private int logo; // resource identifier of activity's logo.
|
||||
private int theme; // resource identifier of activity's theme.
|
||||
private int windowFlags; // custom window flags for preview window.
|
||||
private Task task; // the task this is in.
|
||||
private long createTime = System.currentTimeMillis();
|
||||
long lastVisibleTime; // last time this activity became visible
|
||||
@@ -735,7 +731,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// Note: these are de-referenced before the starting window animates away.
|
||||
StartingData mStartingData;
|
||||
WindowState mStartingWindow;
|
||||
WindowManagerPolicy.StartingSurface mStartingSurface;
|
||||
StartingSurfaceController.StartingSurface mStartingSurface;
|
||||
boolean startingDisplayed;
|
||||
boolean startingMoved;
|
||||
|
||||
@@ -1742,11 +1738,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
labelRes = app.labelRes;
|
||||
}
|
||||
icon = aInfo.getIconResource();
|
||||
logo = aInfo.getLogoResource();
|
||||
theme = aInfo.getThemeResource();
|
||||
if ((aInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
|
||||
windowFlags |= LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
||||
}
|
||||
if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
|
||||
&& (aInfo.applicationInfo.uid == SYSTEM_UID
|
||||
|| aInfo.applicationInfo.uid == _caller.mInfo.uid)) {
|
||||
@@ -1979,33 +1971,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return true;
|
||||
}
|
||||
|
||||
private void applyStartingWindowTheme(String pkg, int theme) {
|
||||
if (theme != 0) {
|
||||
AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
|
||||
com.android.internal.R.styleable.Window,
|
||||
mWmService.mCurrentUserId);
|
||||
if (ent == null) {
|
||||
return;
|
||||
}
|
||||
final boolean windowShowWallpaper = ent.array.getBoolean(
|
||||
com.android.internal.R.styleable.Window_windowShowWallpaper, false);
|
||||
if (windowShowWallpaper && 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
|
||||
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
|
||||
ActivityRecord from, boolean newTask, boolean taskSwitch, boolean processRunning,
|
||||
boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty,
|
||||
boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,
|
||||
boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,
|
||||
boolean activityCreated, boolean useEmpty,
|
||||
boolean activityAllDrawn) {
|
||||
// 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.
|
||||
@@ -2062,7 +2031,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
if (resolvedTheme == 0 && theme != 0) {
|
||||
return false;
|
||||
}
|
||||
applyStartingWindowTheme(pkg, resolvedTheme);
|
||||
|
||||
if (from != null && transferStartingWindow(from)) {
|
||||
return true;
|
||||
@@ -2075,9 +2043,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");
|
||||
mStartingData = new SplashScreenStartingData(mWmService, pkg,
|
||||
resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
|
||||
getMergedOverrideConfiguration(), typeParameter);
|
||||
mStartingData = new SplashScreenStartingData(mWmService, resolvedTheme, typeParameter);
|
||||
scheduleAddStartingWindow();
|
||||
return true;
|
||||
}
|
||||
@@ -2099,17 +2065,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
void scheduleAddStartingWindow() {
|
||||
if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
mAddStartingWindow.run();
|
||||
} else {
|
||||
// Note: we really want to do sendMessageAtFrontOfQueue() because we
|
||||
// want to process the message ASAP, before any other queued
|
||||
// messages.
|
||||
if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
|
||||
mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
|
||||
}
|
||||
}
|
||||
mAddStartingWindow.run();
|
||||
}
|
||||
|
||||
private class AddStartingWindow implements Runnable {
|
||||
@@ -2120,9 +2076,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
final StartingData startingData;
|
||||
synchronized (mWmService.mGlobalLock) {
|
||||
// There can only be one adding request, silly caller!
|
||||
if (!StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
mWmService.mAnimationHandler.removeCallbacks(this);
|
||||
}
|
||||
|
||||
if (mStartingData == null) {
|
||||
// Animation has been canceled... do nothing.
|
||||
@@ -2137,7 +2090,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Add starting %s: startingData=%s",
|
||||
this, startingData);
|
||||
|
||||
WindowManagerPolicy.StartingSurface surface = null;
|
||||
StartingSurfaceController.StartingSurface surface = null;
|
||||
try {
|
||||
surface = startingData.createStartingSurface(ActivityRecord.this);
|
||||
} catch (Exception e) {
|
||||
@@ -2250,9 +2203,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
private boolean transferSplashScreenIfNeeded() {
|
||||
if (!mWmService.mStartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
return false;
|
||||
}
|
||||
if (!mHandleExitSplashScreen || mStartingSurface == null || mStartingWindow == null
|
||||
|| mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
|
||||
return false;
|
||||
@@ -2393,7 +2343,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowManagerPolicy.StartingSurface surface;
|
||||
final StartingSurfaceController.StartingSurface surface;
|
||||
final StartingData startingData = mStartingData;
|
||||
if (mStartingData != null) {
|
||||
surface = mStartingSurface;
|
||||
@@ -2427,13 +2377,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
};
|
||||
|
||||
if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
removeSurface.run();
|
||||
} else {
|
||||
// Use the same thread to remove the window as we used to add it, as otherwise we end up
|
||||
// with things in the view hierarchy being called from different threads.
|
||||
mWmService.mAnimationHandler.post(removeSurface);
|
||||
}
|
||||
removeSurface.run();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6540,9 +6484,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return;
|
||||
}
|
||||
|
||||
final CompatibilityInfo compatInfo =
|
||||
mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);
|
||||
|
||||
mSplashScreenStyleEmpty = shouldUseEmptySplashScreen(sourceRecord, startActivity);
|
||||
|
||||
final int splashScreenTheme = startActivity ? getSplashscreenTheme() : 0;
|
||||
@@ -6557,7 +6498,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
&& task.getActivity((r) -> !r.finishing && r != this) == null;
|
||||
|
||||
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
|
||||
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
|
||||
prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
|
||||
allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty, allDrawn);
|
||||
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.android.server.wm;
|
||||
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
import com.android.server.wm.StartingSurfaceController.StartingSurface;
|
||||
|
||||
/**
|
||||
* Represents starting data for snapshot starting windows.
|
||||
|
||||
@@ -16,47 +16,25 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
import com.android.server.wm.StartingSurfaceController.StartingSurface;
|
||||
|
||||
/**
|
||||
* Represents starting data for splash screens, i.e. "traditional" starting windows.
|
||||
*/
|
||||
class SplashScreenStartingData extends StartingData {
|
||||
|
||||
private final String mPkg;
|
||||
private final int mTheme;
|
||||
private final CompatibilityInfo mCompatInfo;
|
||||
private final CharSequence mNonLocalizedLabel;
|
||||
private final int mLabelRes;
|
||||
private final int mIcon;
|
||||
private final int mLogo;
|
||||
private final int mWindowFlags;
|
||||
private final Configuration mMergedOverrideConfiguration;
|
||||
|
||||
SplashScreenStartingData(WindowManagerService service, String pkg, int theme,
|
||||
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
|
||||
int logo, int windowFlags, Configuration mergedOverrideConfiguration, int typeParams) {
|
||||
SplashScreenStartingData(WindowManagerService service, int theme,
|
||||
int typeParams) {
|
||||
super(service, typeParams);
|
||||
mPkg = pkg;
|
||||
mTheme = theme;
|
||||
mCompatInfo = compatInfo;
|
||||
mNonLocalizedLabel = nonLocalizedLabel;
|
||||
mLabelRes = labelRes;
|
||||
mIcon = icon;
|
||||
mLogo = logo;
|
||||
mWindowFlags = windowFlags;
|
||||
mMergedOverrideConfiguration = mergedOverrideConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
StartingSurface createStartingSurface(ActivityRecord activity) {
|
||||
return mService.mStartingSurfaceController.createSplashScreenStartingSurface(
|
||||
activity, mPkg, mTheme, mCompatInfo, mNonLocalizedLabel, mLabelRes, mIcon,
|
||||
mLogo, mWindowFlags, mMergedOverrideConfiguration,
|
||||
activity.getDisplayContent().getDisplayId());
|
||||
activity, mTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
import com.android.server.wm.StartingSurfaceController.StartingSurface;
|
||||
|
||||
/**
|
||||
* Represents the model about how a starting window should be constructed.
|
||||
|
||||
@@ -31,14 +31,9 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Slog;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -47,9 +42,6 @@ import java.util.function.Supplier;
|
||||
public class StartingSurfaceController {
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME
|
||||
? StartingSurfaceController.class.getSimpleName() : TAG_WM;
|
||||
/** Set to {@code true} to enable shell starting surface drawer. */
|
||||
static final boolean DEBUG_ENABLE_SHELL_DRAWER =
|
||||
SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
|
||||
private final WindowManagerService mService;
|
||||
private final SplashScreenExceptionList mSplashScreenExceptionsList;
|
||||
|
||||
@@ -58,20 +50,13 @@ public class StartingSurfaceController {
|
||||
mSplashScreenExceptionsList = new SplashScreenExceptionList(wm.mContext.getMainExecutor());
|
||||
}
|
||||
|
||||
StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, String packageName,
|
||||
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
|
||||
int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
|
||||
if (!DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName,
|
||||
theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
|
||||
overrideConfig, displayId);
|
||||
}
|
||||
StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, int theme) {
|
||||
|
||||
synchronized (mService.mGlobalLock) {
|
||||
final Task task = activity.getTask();
|
||||
if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
|
||||
task, activity, theme, null /* taskSnapshot */)) {
|
||||
return new ShellStartingSurface(task);
|
||||
return new StartingSurface(task);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -151,24 +136,25 @@ public class StartingSurfaceController {
|
||||
activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
|
||||
topFullscreenActivity, false /* checkOpening */);
|
||||
}
|
||||
if (DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
|
||||
activity, 0 /* launchTheme */, taskSnapshot);
|
||||
return new ShellStartingSurface(task);
|
||||
}
|
||||
return new StartingSurface(task);
|
||||
}
|
||||
return mService.mTaskSnapshotController.createStartingSurface(activity, taskSnapshot);
|
||||
}
|
||||
|
||||
|
||||
private final class ShellStartingSurface implements StartingSurface {
|
||||
final class StartingSurface {
|
||||
private final Task mTask;
|
||||
|
||||
ShellStartingSurface(Task task) {
|
||||
StartingSurface(Task task) {
|
||||
mTask = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Removes the starting window surface. Do not hold the window manager lock when calling
|
||||
* this method!
|
||||
* @param animate Whether need to play the default exit animation for starting window.
|
||||
*/
|
||||
public void remove(boolean animate) {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, animate);
|
||||
|
||||
@@ -16,14 +16,29 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
|
||||
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.RecordingCanvas;
|
||||
@@ -44,11 +59,11 @@ import android.view.WindowInsetsController.Appearance;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.ColorUtils;
|
||||
import com.android.internal.policy.DecorView;
|
||||
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
|
||||
import com.android.server.wm.utils.InsetUtils;
|
||||
|
||||
import com.google.android.collect.Sets;
|
||||
@@ -241,15 +256,6 @@ class TaskSnapshotController {
|
||||
mCache.clearRunningCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
|
||||
* MANAGER LOCK WHEN CALLING THIS METHOD!
|
||||
*/
|
||||
StartingSurface createStartingSurface(ActivityRecord activity,
|
||||
TaskSnapshot snapshot) {
|
||||
return TaskSnapshotSurface.create(mService, activity, snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the window for a given task to take a snapshot. Top child of the task is usually the one
|
||||
* we're looking for, but during app transitions, trampoline activities can appear in the
|
||||
@@ -374,12 +380,6 @@ class TaskSnapshotController {
|
||||
return taskSnapshot;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
|
||||
float scaleFraction, TaskSnapshot.Builder builder) {
|
||||
return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null, builder);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SurfaceControl.ScreenshotHardwareBuffer createImeSnapshot(@NonNull Task task,
|
||||
int pixelFormat) {
|
||||
@@ -573,7 +573,7 @@ class TaskSnapshotController {
|
||||
final RecordingCanvas c = node.start(width, height);
|
||||
c.drawColor(color);
|
||||
decorPainter.setInsets(systemBarInsets);
|
||||
decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
|
||||
decorPainter.drawDecors(c /* statusBarExcludeFrame */);
|
||||
node.end(c);
|
||||
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
|
||||
if (hwBitmap == null) {
|
||||
@@ -704,4 +704,92 @@ class TaskSnapshotController {
|
||||
pw.println(prefix + "mTaskSnapshotEnabled=" + mTaskSnapshotEnabled);
|
||||
mCache.dump(pw, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to draw the background of the system bars in regions the task snapshot isn't
|
||||
* filling the window.
|
||||
*/
|
||||
static class SystemBarBackgroundPainter {
|
||||
|
||||
private final Paint mStatusBarPaint = new Paint();
|
||||
private final Paint mNavigationBarPaint = new Paint();
|
||||
private final int mStatusBarColor;
|
||||
private final int mNavigationBarColor;
|
||||
private final int mWindowFlags;
|
||||
private final int mWindowPrivateFlags;
|
||||
private final float mScale;
|
||||
private final InsetsState mInsetsState;
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
|
||||
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
|
||||
ActivityManager.TaskDescription taskDescription, float scale,
|
||||
InsetsState insetsState) {
|
||||
mWindowFlags = windowFlags;
|
||||
mWindowPrivateFlags = windowPrivateFlags;
|
||||
mScale = scale;
|
||||
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
|
||||
final int semiTransparent = context.getColor(
|
||||
R.color.system_bar_background_semi_transparent);
|
||||
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
|
||||
semiTransparent, taskDescription.getStatusBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_STATUS_BARS,
|
||||
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
||||
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
||||
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
||||
taskDescription.getNavigationBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
||||
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
|
||||
mStatusBarPaint.setColor(mStatusBarColor);
|
||||
mNavigationBarPaint.setColor(mNavigationBarColor);
|
||||
mInsetsState = insetsState;
|
||||
}
|
||||
|
||||
void setInsets(Rect systemBarInsets) {
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
}
|
||||
|
||||
int getStatusBarColorViewHeight() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
|
||||
return (int) (mSystemBarInsets.top * mScale);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNavigationBarColorViewVisible() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground);
|
||||
}
|
||||
|
||||
void drawDecors(Canvas c) {
|
||||
drawStatusBarBackground(c, getStatusBarColorViewHeight());
|
||||
drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawStatusBarBackground(Canvas c,
|
||||
int statusBarHeight) {
|
||||
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0) {
|
||||
final int rightInset = (int) (mSystemBarInsets.right * mScale);
|
||||
c.drawRect(0, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
final Rect navigationBarRect = new Rect();
|
||||
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
|
||||
mScale);
|
||||
final boolean visible = isNavigationBarColorViewVisible();
|
||||
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
|
||||
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,620 +0,0 @@
|
||||
/*
|
||||
* 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.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.graphics.Color.WHITE;
|
||||
import static android.graphics.Color.alpha;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
|
||||
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
|
||||
import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.MergedConfiguration;
|
||||
import android.util.Slog;
|
||||
import android.view.IWindowSession;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.InsetsVisibilities;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.ClientWindowFrames;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.policy.DecorView;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.view.BaseIWindow;
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
|
||||
/**
|
||||
* This class represents a starting window that shows a snapshot.
|
||||
* <p>
|
||||
* DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS!
|
||||
*/
|
||||
class TaskSnapshotSurface implements StartingSurface {
|
||||
|
||||
private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
|
||||
|
||||
/**
|
||||
* When creating the starting window, we use the exact same layout flags such that we end up
|
||||
* with a window with the exact same dimensions etc. However, these flags are not used in layout
|
||||
* and might cause other side effects so we exclude them.
|
||||
*/
|
||||
static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE
|
||||
| FLAG_NOT_TOUCH_MODAL
|
||||
| FLAG_ALT_FOCUSABLE_IM
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_HARDWARE_ACCELERATED
|
||||
| FLAG_IGNORE_CHEEK_PRESSES
|
||||
| FLAG_LOCAL_FOCUS_MODE
|
||||
| FLAG_SLIPPERY
|
||||
| FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| FLAG_SPLIT_TOUCH
|
||||
| FLAG_SCALED
|
||||
| FLAG_SECURE;
|
||||
|
||||
private static final int PRIVATE_FLAG_INHERITS = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
|
||||
private static final int MSG_REPORT_DRAW = 0;
|
||||
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
|
||||
|
||||
//tmp vars for unused relayout params
|
||||
private static final Point sTmpSurfaceSize = new Point();
|
||||
|
||||
private final Window mWindow;
|
||||
private final SurfaceControl mSurfaceControl;
|
||||
private final IWindowSession mSession;
|
||||
private final WindowManagerService mService;
|
||||
private final int mDisplayId;
|
||||
private final Rect mTaskBounds;
|
||||
private final Rect mFrame = new Rect();
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
private TaskSnapshot mSnapshot;
|
||||
private final RectF mTmpSnapshotSize = new RectF();
|
||||
private final RectF mTmpDstFrame = new RectF();
|
||||
private final CharSequence mTitle;
|
||||
private boolean mHasDrawn;
|
||||
private long mShownTime;
|
||||
private final Handler mHandler;
|
||||
private boolean mSizeMismatch;
|
||||
private final Paint mBackgroundPaint = new Paint();
|
||||
private final int mActivityType;
|
||||
private final int mStatusBarColor;
|
||||
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
||||
private final int mOrientationOnCreation;
|
||||
private final SurfaceControl.Transaction mTransaction;
|
||||
private final Matrix mSnapshotMatrix = new Matrix();
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
|
||||
TaskSnapshot snapshot) {
|
||||
return create(service, activity, snapshot, WindowManagerGlobal.getWindowSession());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
|
||||
TaskSnapshot snapshot, IWindowSession session) {
|
||||
|
||||
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
|
||||
final Window window = new Window();
|
||||
window.setSession(session);
|
||||
final SurfaceControl surfaceControl = new SurfaceControl();
|
||||
final ClientWindowFrames tmpFrames = new ClientWindowFrames();
|
||||
final Rect taskBounds;
|
||||
final InsetsState mTmpInsetsState = new InsetsState();
|
||||
final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
|
||||
final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
|
||||
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
|
||||
final TaskDescription taskDescription = new TaskDescription();
|
||||
taskDescription.setBackgroundColor(WHITE);
|
||||
final WindowState topFullscreenOpaqueWindow;
|
||||
final int appearance;
|
||||
final int windowFlags;
|
||||
final int windowPrivateFlags;
|
||||
final int currentOrientation;
|
||||
final int activityType;
|
||||
final InsetsState insetsState;
|
||||
synchronized (service.mGlobalLock) {
|
||||
final WindowState mainWindow = activity.findMainWindow();
|
||||
final Task task = activity.getTask();
|
||||
final ActivityRecord topFullscreenActivity =
|
||||
activity.getTask().getTopFullscreenActivity();
|
||||
// Already check the nullity in StartingSurfaceController#createTaskSnapshotSurface
|
||||
topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow();
|
||||
WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs;
|
||||
appearance = attrs.insetsFlags.appearance;
|
||||
windowFlags = attrs.flags;
|
||||
windowPrivateFlags = attrs.privateFlags;
|
||||
|
||||
layoutParams.packageName = mainWindow.getAttrs().packageName;
|
||||
layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations;
|
||||
layoutParams.dimAmount = mainWindow.getAttrs().dimAmount;
|
||||
layoutParams.type = TYPE_APPLICATION_STARTING;
|
||||
layoutParams.format = snapshot.getHardwareBuffer().getFormat();
|
||||
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE;
|
||||
// Setting as trusted overlay to let touches pass through. This is safe because this
|
||||
// window is controlled by the system.
|
||||
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_INHERITS)
|
||||
| PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
|
||||
layoutParams.token = activity.token;
|
||||
layoutParams.width = LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = LayoutParams.MATCH_PARENT;
|
||||
layoutParams.insetsFlags.appearance = appearance;
|
||||
layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
|
||||
layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
|
||||
layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
|
||||
layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
|
||||
layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
|
||||
|
||||
layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
|
||||
|
||||
final TaskDescription td = task.getTaskDescription();
|
||||
if (td != null) {
|
||||
taskDescription.copyFromPreserveHiddenFields(td);
|
||||
}
|
||||
taskBounds = new Rect();
|
||||
task.getBounds(taskBounds);
|
||||
currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
|
||||
activityType = activity.getActivityType();
|
||||
insetsState = topFullscreenOpaqueWindow.getInsetsStateWithVisibilityOverride();
|
||||
|
||||
}
|
||||
int displayId = activity.getDisplayContent().getDisplayId();
|
||||
try {
|
||||
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
|
||||
mRequestedVisibilities, null /* outInputChannel */, mTmpInsetsState,
|
||||
mTempControls);
|
||||
if (res < 0) {
|
||||
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
|
||||
return null;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// Local call.
|
||||
}
|
||||
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, displayId,
|
||||
window, surfaceControl, snapshot, layoutParams.getTitle(), taskDescription,
|
||||
appearance, windowFlags, windowPrivateFlags, taskBounds, currentOrientation,
|
||||
activityType, insetsState);
|
||||
window.setOuter(snapshotSurface);
|
||||
try {
|
||||
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
|
||||
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
|
||||
mTempControls, sTmpSurfaceSize);
|
||||
} catch (RemoteException e) {
|
||||
// Local call.
|
||||
}
|
||||
|
||||
final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, insetsState);
|
||||
snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
|
||||
snapshotSurface.drawSnapshot();
|
||||
return snapshotSurface;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
TaskSnapshotSurface(WindowManagerService service, int displayId, Window window,
|
||||
SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title,
|
||||
TaskDescription taskDescription, int appearance, int windowFlags,
|
||||
int windowPrivateFlags, Rect taskBounds, int currentOrientation, int activityType,
|
||||
InsetsState insetsState) {
|
||||
mService = service;
|
||||
mDisplayId = displayId;
|
||||
mHandler = new Handler(mService.mH.getLooper());
|
||||
mSession = WindowManagerGlobal.getWindowSession();
|
||||
mWindow = window;
|
||||
mSurfaceControl = surfaceControl;
|
||||
mSnapshot = snapshot;
|
||||
mTitle = title;
|
||||
int backgroundColor = taskDescription.getBackgroundColor();
|
||||
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
||||
mTaskBounds = taskBounds;
|
||||
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
|
||||
windowPrivateFlags, appearance, taskDescription, 1f, insetsState);
|
||||
mStatusBarColor = taskDescription.getStatusBarColor();
|
||||
mOrientationOnCreation = currentOrientation;
|
||||
mActivityType = activityType;
|
||||
mTransaction = mService.mTransactionFactory.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(boolean animate) {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
|
||||
// Show the latest content as soon as possible for unlocking to home.
|
||||
&& mActivityType != ACTIVITY_TYPE_HOME) {
|
||||
mHandler.postAtTime(() -> remove(false /* prepareAnimation */),
|
||||
mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
|
||||
"Defer removing snapshot surface in %dms", (now - mShownTime));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing snapshot surface");
|
||||
mSession.remove(mWindow);
|
||||
} catch (RemoteException e) {
|
||||
// Local call.
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setFrames(Rect frame, Rect systemBarInsets) {
|
||||
mFrame.set(frame);
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
mSizeMismatch = (mFrame.width() != snapshot.getWidth()
|
||||
|| mFrame.height() != snapshot.getHeight());
|
||||
mSystemBarBackgroundPainter.setInsets(systemBarInsets);
|
||||
}
|
||||
|
||||
private void drawSnapshot() {
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Drawing snapshot surface sizeMismatch=%b",
|
||||
mSizeMismatch);
|
||||
if (mSizeMismatch) {
|
||||
// The dimensions of the buffer and the window don't match, so attaching the buffer
|
||||
// will fail. Better create a child window with the exact dimensions and fill the parent
|
||||
// window with the background color!
|
||||
drawSizeMismatchSnapshot();
|
||||
} else {
|
||||
drawSizeMatchSnapshot();
|
||||
}
|
||||
synchronized (mService.mGlobalLock) {
|
||||
mShownTime = SystemClock.uptimeMillis();
|
||||
mHasDrawn = true;
|
||||
}
|
||||
reportDrawn();
|
||||
|
||||
// In case window manager leaks us, make sure we don't retain the snapshot.
|
||||
mSnapshot = null;
|
||||
}
|
||||
|
||||
private void drawSizeMatchSnapshot() {
|
||||
GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
|
||||
mSnapshot.getHardwareBuffer());
|
||||
mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
|
||||
.setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void drawSizeMismatchSnapshot() {
|
||||
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
|
||||
final SurfaceSession session = new SurfaceSession();
|
||||
// We consider nearly matched dimensions as there can be rounding errors and the user won't
|
||||
// notice very minute differences from scaling one dimension more than the other
|
||||
final boolean aspectRatioMismatch = Math.abs(
|
||||
((float) buffer.getWidth() / buffer.getHeight())
|
||||
- ((float) mFrame.width() / mFrame.height())) > 0.01f;
|
||||
|
||||
// Keep a reference to it such that it doesn't get destroyed when finalized.
|
||||
final String name = mTitle + " - task-snapshot-surface";
|
||||
SurfaceControl childSurfaceControl = mService.mSurfaceControlFactory.apply(session)
|
||||
.setName(name)
|
||||
.setBLASTLayer()
|
||||
.setFormat(buffer.getFormat())
|
||||
.setParent(mSurfaceControl)
|
||||
.setCallsite("TaskSnapshotSurface.drawSizeMismatchSnapshot")
|
||||
.build();
|
||||
|
||||
final Rect frame;
|
||||
// We can just show the surface here as it will still be hidden as the parent is
|
||||
// still hidden.
|
||||
mTransaction.show(childSurfaceControl);
|
||||
if (aspectRatioMismatch) {
|
||||
// Clip off ugly navigation bar.
|
||||
final Rect crop = calculateSnapshotCrop();
|
||||
frame = calculateSnapshotFrame(crop);
|
||||
mTransaction.setWindowCrop(childSurfaceControl, crop);
|
||||
mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
|
||||
mTmpSnapshotSize.set(crop);
|
||||
mTmpDstFrame.set(frame);
|
||||
} else {
|
||||
frame = null;
|
||||
mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
mTmpDstFrame.set(mFrame);
|
||||
mTmpDstFrame.offsetTo(0, 0);
|
||||
}
|
||||
|
||||
// Scale the mismatch dimensions to fill the task bounds
|
||||
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
|
||||
mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
|
||||
|
||||
GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
|
||||
mSnapshot.getHardwareBuffer());
|
||||
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
|
||||
mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
|
||||
// This is the way to tell the input system to exclude this surface from occlusion
|
||||
// detection since we don't have a window for it. We do this because this window is
|
||||
// generated by the system as well as its content (the snapshot of the app).
|
||||
InputMonitor.setTrustedOverlayInputInfo(childSurfaceControl, mTransaction, mDisplayId,
|
||||
name);
|
||||
|
||||
if (aspectRatioMismatch) {
|
||||
GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
|
||||
PixelFormat.RGBA_8888,
|
||||
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
|
||||
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
|
||||
final Canvas c = background.lockCanvas();
|
||||
drawBackgroundAndBars(c, frame);
|
||||
background.unlockCanvasAndPost(c);
|
||||
mTransaction.setBuffer(mSurfaceControl, background);
|
||||
}
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot crop in snapshot coordinate space.
|
||||
*
|
||||
* @return crop rect in snapshot coordinate space.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Rect calculateSnapshotCrop() {
|
||||
final Rect rect = new Rect();
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
|
||||
final Rect insets = mSnapshot.getContentInsets();
|
||||
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Let's remove all system decorations except the status bar, but only if the task is at the
|
||||
// very top of the screen.
|
||||
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
|
||||
rect.inset((int) (insets.left * scaleX),
|
||||
isTop ? 0 : (int) (insets.top * scaleY),
|
||||
(int) (insets.right * scaleX),
|
||||
(int) (insets.bottom * scaleY));
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot frame in window coordinate space from crop.
|
||||
*
|
||||
* @param crop rect that is in snapshot coordinate space.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Rect calculateSnapshotFrame(Rect crop) {
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Rescale the frame from snapshot to window coordinate space
|
||||
final Rect frame = new Rect(0, 0,
|
||||
(int) (crop.width() / scaleX + 0.5f),
|
||||
(int) (crop.height() / scaleY + 0.5f)
|
||||
);
|
||||
|
||||
// However, we also need to make space for the navigation bar on the left side.
|
||||
frame.offset(mSystemBarInsets.left, 0);
|
||||
return frame;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawBackgroundAndBars(Canvas c, Rect frame) {
|
||||
final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
|
||||
final boolean fillHorizontally = c.getWidth() > frame.right;
|
||||
final boolean fillVertically = c.getHeight() > frame.bottom;
|
||||
if (fillHorizontally) {
|
||||
c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
|
||||
c.getWidth(), fillVertically
|
||||
? frame.bottom
|
||||
: c.getHeight(),
|
||||
mBackgroundPaint);
|
||||
}
|
||||
if (fillVertically) {
|
||||
c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
|
||||
}
|
||||
mSystemBarBackgroundPainter.drawDecors(c, frame);
|
||||
}
|
||||
|
||||
private void reportDrawn() {
|
||||
try {
|
||||
mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
|
||||
} catch (RemoteException e) {
|
||||
// Local call.
|
||||
}
|
||||
}
|
||||
|
||||
private static Handler sHandler = new Handler(Looper.getMainLooper()) {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_REPORT_DRAW:
|
||||
final boolean hasDrawn;
|
||||
final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
|
||||
synchronized (surface.mService.mGlobalLock) {
|
||||
hasDrawn = surface.mHasDrawn;
|
||||
}
|
||||
if (hasDrawn) {
|
||||
surface.reportDrawn();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
static class Window extends BaseIWindow {
|
||||
|
||||
private TaskSnapshotSurface mOuter;
|
||||
|
||||
public void setOuter(TaskSnapshotSurface outer) {
|
||||
mOuter = outer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resized(ClientWindowFrames frames, boolean reportDraw,
|
||||
MergedConfiguration mergedConfiguration, boolean forceLayout,
|
||||
boolean alwaysConsumeSystemBars, int displayId) {
|
||||
if (mergedConfiguration != null && mOuter != null
|
||||
&& mOuter.mOrientationOnCreation
|
||||
!= mergedConfiguration.getMergedConfiguration().orientation) {
|
||||
|
||||
// The orientation of the screen is changing. We better remove the snapshot ASAP as
|
||||
// we are going to wait on the new window in any case to unfreeze the screen, and
|
||||
// the starting window is not needed anymore.
|
||||
sHandler.post(() -> mOuter.remove(false /* prepareAnimation */));
|
||||
}
|
||||
if (reportDraw) {
|
||||
sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to draw the background of the system bars in regions the task snapshot isn't
|
||||
* filling the window.
|
||||
*/
|
||||
static class SystemBarBackgroundPainter {
|
||||
|
||||
private final Paint mStatusBarPaint = new Paint();
|
||||
private final Paint mNavigationBarPaint = new Paint();
|
||||
private final int mStatusBarColor;
|
||||
private final int mNavigationBarColor;
|
||||
private final int mWindowFlags;
|
||||
private final int mWindowPrivateFlags;
|
||||
private final float mScale;
|
||||
private final InsetsState mInsetsState;
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
|
||||
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
|
||||
TaskDescription taskDescription, float scale, InsetsState insetsState) {
|
||||
mWindowFlags = windowFlags;
|
||||
mWindowPrivateFlags = windowPrivateFlags;
|
||||
mScale = scale;
|
||||
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
|
||||
final int semiTransparent = context.getColor(
|
||||
R.color.system_bar_background_semi_transparent);
|
||||
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
|
||||
semiTransparent, taskDescription.getStatusBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_STATUS_BARS,
|
||||
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
||||
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
||||
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
||||
taskDescription.getNavigationBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
||||
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
|
||||
mStatusBarPaint.setColor(mStatusBarColor);
|
||||
mNavigationBarPaint.setColor(mNavigationBarColor);
|
||||
mInsetsState = insetsState;
|
||||
}
|
||||
|
||||
void setInsets(Rect systemBarInsets) {
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
}
|
||||
|
||||
int getStatusBarColorViewHeight() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
|
||||
return (int) (mSystemBarInsets.top * mScale);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNavigationBarColorViewVisible() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground);
|
||||
}
|
||||
|
||||
void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
|
||||
drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
|
||||
int statusBarHeight) {
|
||||
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
|
||||
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
|
||||
final int rightInset = (int) (mSystemBarInsets.right * mScale);
|
||||
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
|
||||
c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
final Rect navigationBarRect = new Rect();
|
||||
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
|
||||
mScale);
|
||||
final boolean visible = isNavigationBarColorViewVisible();
|
||||
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
|
||||
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2490,9 +2490,8 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
public void testCreateRemoveStartingWindow() {
|
||||
registerTestStartingWindowOrganizer();
|
||||
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
activity.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activity.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertHasStartingWindow(activity);
|
||||
activity.removeStartingWindow();
|
||||
@@ -2503,9 +2502,8 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
private void testLegacySplashScreen(int targetSdk, int verifyType) {
|
||||
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
activity.mTargetSdk = targetSdk;
|
||||
activity.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activity.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertHasStartingWindow(activity);
|
||||
assertEquals(activity.mStartingData.mTypeParams & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN,
|
||||
@@ -2541,13 +2539,11 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
.setVisible(false).build();
|
||||
final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true)
|
||||
.setVisible(false).build();
|
||||
activity1.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
activity2.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
|
||||
true, true, false, true, false, false, false);
|
||||
activity2.addStartingWindow(mPackageName, android.R.style.Theme, activity1, true, true,
|
||||
false, true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertFalse(mDisplayContent.mSkipAppTransitionAnimation);
|
||||
assertNoStartingWindow(activity1);
|
||||
@@ -2562,14 +2558,11 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
organizer.setRunnableWhenAddingSplashScreen(
|
||||
() -> {
|
||||
// Surprise, ...! Transfer window in the middle of the creation flow.
|
||||
activity2.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0,
|
||||
activity1, true, true, false,
|
||||
true, false, false, false);
|
||||
activity2.addStartingWindow(mPackageName, android.R.style.Theme, activity1,
|
||||
true, true, false, true, false, false, false);
|
||||
});
|
||||
activity1.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertNoStartingWindow(activity1);
|
||||
assertHasStartingWindow(activity2);
|
||||
@@ -2580,13 +2573,11 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
registerTestStartingWindowOrganizer();
|
||||
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
activity1.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
activity2.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
|
||||
true, true, false, true, false, false, false);
|
||||
activity2.addStartingWindow(mPackageName, android.R.style.Theme, activity1, true, true,
|
||||
false, true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertNoStartingWindow(activity1);
|
||||
assertHasStartingWindow(activity2);
|
||||
@@ -2624,11 +2615,10 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
registerTestStartingWindowOrganizer();
|
||||
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final Task task = activity.getTask();
|
||||
activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
|
||||
"Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */,
|
||||
null /* transferFrom */, true /* newTask */, true /* taskSwitch */,
|
||||
false /* processRunning */, false /* allowTaskSnapshot */,
|
||||
false /* activityCreate */, false /* suggestEmpty */, false /* activityAllDrawn */);
|
||||
activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* transferFrom */,
|
||||
true /* newTask */, true /* taskSwitch */, false /* processRunning */,
|
||||
false /* allowTaskSnapshot */, false /* activityCreate */, false /* suggestEmpty
|
||||
*/, false /* activityAllDrawn */);
|
||||
waitUntilHandlersIdle();
|
||||
assertHasStartingWindow(activity);
|
||||
|
||||
@@ -2674,9 +2664,8 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
|
||||
topActivity.setVisible(false);
|
||||
task.positionChildAt(topActivity, POSITION_TOP);
|
||||
activity.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activity.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
|
||||
// Make activities to have different rotation from it display and set fixed rotation
|
||||
@@ -2691,9 +2680,8 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
doReturn(true).when(activity).isAnimating(anyInt());
|
||||
// Make sure the fixed rotation transform linked to activity2 when adding starting window
|
||||
// on activity2.
|
||||
topActivity.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity,
|
||||
false, false, false, true, false, false, false);
|
||||
topActivity.addStartingWindow(mPackageName, android.R.style.Theme, activity, false, false,
|
||||
false, true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertTrue(topActivity.hasFixedRotationTransform());
|
||||
}
|
||||
@@ -2707,9 +2695,8 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
activityTop.getTask().addChild(activityBottom, 0);
|
||||
|
||||
// Add a starting window.
|
||||
activityTop.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
|
||||
false, false, false);
|
||||
activityTop.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
|
||||
true, false, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
|
||||
// Make the top one invisible, and try transferring the starting window from the top to the
|
||||
|
||||
@@ -85,7 +85,7 @@ public class DisplayAreaPolicyBuilderTest {
|
||||
@Rule
|
||||
public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
|
||||
|
||||
private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
|
||||
private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
|
||||
private WindowManagerService mWms;
|
||||
private RootDisplayArea mRoot;
|
||||
private DisplayArea.Tokens mImeContainer;
|
||||
|
||||
@@ -99,7 +99,6 @@ public class SystemServicesTestRule implements TestRule {
|
||||
private static final String TAG = SystemServicesTestRule.class.getSimpleName();
|
||||
|
||||
static int sNextDisplayId = DEFAULT_DISPLAY + 100;
|
||||
static int sNextTaskId = 100;
|
||||
|
||||
private static final int[] TEST_USER_PROFILE_IDS = {};
|
||||
|
||||
@@ -109,8 +108,6 @@ public class SystemServicesTestRule implements TestRule {
|
||||
private ActivityManagerService mAmService;
|
||||
private ActivityTaskManagerService mAtmService;
|
||||
private WindowManagerService mWmService;
|
||||
private TestWindowManagerPolicy mWMPolicy;
|
||||
private TestDisplayWindowSettingsProvider mTestDisplayWindowSettingsProvider;
|
||||
private WindowState.PowerManagerWrapper mPowerManagerWrapper;
|
||||
private InputManagerService mImService;
|
||||
private InputChannel mInputChannel;
|
||||
@@ -283,14 +280,14 @@ public class SystemServicesTestRule implements TestRule {
|
||||
|
||||
private void setUpWindowManagerService() {
|
||||
mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
|
||||
mWMPolicy = new TestWindowManagerPolicy(this::getWindowManagerService,
|
||||
mPowerManagerWrapper);
|
||||
mTestDisplayWindowSettingsProvider = new TestDisplayWindowSettingsProvider();
|
||||
TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy();
|
||||
TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider =
|
||||
new TestDisplayWindowSettingsProvider();
|
||||
// Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
|
||||
DisplayThread.getHandler().post(StrictMode::allowThreadDiskWritesMask);
|
||||
mWmService = WindowManagerService.main(
|
||||
mContext, mImService, false, false, mWMPolicy, mAtmService,
|
||||
mTestDisplayWindowSettingsProvider, StubTransaction::new,
|
||||
mContext, mImService, false, false, wmPolicy, mAtmService,
|
||||
testDisplayWindowSettingsProvider, StubTransaction::new,
|
||||
() -> mSurfaceFactory.get(), (unused) -> new MockSurfaceControlBuilder());
|
||||
spyOn(mWmService);
|
||||
spyOn(mWmService.mRoot);
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorSpace;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.Display;
|
||||
import android.view.IWindowSession;
|
||||
import android.view.InsetsState;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.wm.TaskSnapshotSurface.Window;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test class for {@link TaskSnapshotSurface}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:TaskSnapshotSurfaceTest
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(WindowTestRunner.class)
|
||||
public class TaskSnapshotSurfaceTest extends WindowTestsBase {
|
||||
|
||||
private TaskSnapshotSurface mSurface;
|
||||
|
||||
private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
|
||||
int windowFlags, Rect taskBounds) {
|
||||
// Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
|
||||
// this behavior set the taskSize to be the same as the taskBounds width and height. The
|
||||
// taskBounds passed here are assumed to be the same task bounds as when the snapshot was
|
||||
// taken. We assume there is no aspect ratio mismatch between the screenshot and the
|
||||
// taskBounds
|
||||
assertEquals(width, taskBounds.width());
|
||||
assertEquals(height, taskBounds.height());
|
||||
Point taskSize = new Point(taskBounds.width(), taskBounds.height());
|
||||
|
||||
final TaskSnapshot snapshot = createTaskSnapshot(width, height, taskSize, contentInsets);
|
||||
mSurface = new TaskSnapshotSurface(mWm, Display.DEFAULT_DISPLAY, new Window(),
|
||||
new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE,
|
||||
Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, taskBounds, ORIENTATION_PORTRAIT,
|
||||
ACTIVITY_TYPE_STANDARD, new InsetsState());
|
||||
}
|
||||
|
||||
private TaskSnapshot createTaskSnapshot(int width, int height,
|
||||
Point taskSize, Rect contentInsets) {
|
||||
final HardwareBuffer buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
|
||||
1, HardwareBuffer.USAGE_CPU_READ_RARELY);
|
||||
return new TaskSnapshot(
|
||||
System.currentTimeMillis(),
|
||||
new ComponentName("", ""), buffer,
|
||||
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
|
||||
Surface.ROTATION_0, taskSize, contentInsets, false,
|
||||
true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
|
||||
0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */);
|
||||
}
|
||||
|
||||
private static TaskDescription createTaskDescription(int background, int statusBar,
|
||||
int navigationBar) {
|
||||
final TaskDescription td = new TaskDescription();
|
||||
td.setBackgroundColor(background);
|
||||
td.setStatusBarColor(statusBar);
|
||||
td.setNavigationBarColor(navigationBar);
|
||||
return td;
|
||||
}
|
||||
|
||||
private void setupSurface(int width, int height) {
|
||||
setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, width, height));
|
||||
}
|
||||
|
||||
private boolean isTrustedOverlay(WindowManager.LayoutParams params) {
|
||||
return (params.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mSystemServicesTestRule.setSurfaceFactory(() -> {
|
||||
Surface surface = mock(Surface.class);
|
||||
when(surface.isValid()).thenReturn(true);
|
||||
return surface;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSurface_asTrustedOverlay() throws Exception {
|
||||
Point task = new Point(200, 100);
|
||||
ActivityRecord activityRecord = createActivityRecord(mDisplayContent);
|
||||
createWindow(null, TYPE_BASE_APPLICATION, activityRecord, "window");
|
||||
TaskSnapshot taskSnapshot = createTaskSnapshot(task.x, task.y, task, new Rect());
|
||||
IWindowSession session = mock(IWindowSession.class);
|
||||
|
||||
TaskSnapshotSurface surface = TaskSnapshotSurface.create(mWm, activityRecord, taskSnapshot,
|
||||
session);
|
||||
|
||||
assertThat(surface).isNotNull();
|
||||
verify(session).addToDisplay(any(), argThat(this::isTrustedOverlay), anyInt(), anyInt(),
|
||||
any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_fillHorizontally() {
|
||||
setupSurface(200, 100);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(200);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
|
||||
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_fillVertically() {
|
||||
setupSurface(100, 200);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(200);
|
||||
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_fillBoth() {
|
||||
setupSurface(200, 200);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(200);
|
||||
when(mockCanvas.getHeight()).thenReturn(200);
|
||||
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_dontFill_sameSize() {
|
||||
setupSurface(100, 100);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_dontFill_bitmapLarger() {
|
||||
setupSurface(100, 100);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_taskNotOnTop() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
|
||||
assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_navBarLeft() {
|
||||
setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_navBarRight() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_waterfall() {
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(5, 0, 95, 90), mSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
assertEquals(new Rect(0, 0, 100, 80),
|
||||
mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame_navBarLeft() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(10, 10, 0, 0);
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
assertEquals(new Rect(10, 0, 100, 90),
|
||||
mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame_waterfall() {
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
mSurface.setFrames(new Rect(5, 0, 95, 100), insets);
|
||||
assertEquals(new Rect(0, 0, 90, 90),
|
||||
mSurface.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground(
|
||||
mockCanvas, new Rect(0, 0, 50, 100), 10);
|
||||
verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground_nullFrame() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground(
|
||||
mockCanvas, null, 10);
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground_nope() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground(
|
||||
mockCanvas, new Rect(0, 0, 100, 100), 10);
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground() {
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground_left() {
|
||||
final Rect insets = new Rect(10, 10, 0, 0);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground_right() {
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,16 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager.GoToSleepReason;
|
||||
import android.os.PowerManager.WakeReason;
|
||||
import android.os.RemoteException;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.IWindow;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.WindowManager;
|
||||
@@ -41,30 +34,19 @@ import android.view.animation.Animation;
|
||||
import com.android.internal.policy.IKeyguardDismissCallback;
|
||||
import com.android.internal.policy.IShortcutService;
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
import com.android.server.wm.WindowState.PowerManagerWrapper;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class TestWindowManagerPolicy implements WindowManagerPolicy {
|
||||
private final Supplier<WindowManagerService> mWmSupplier;
|
||||
private final PowerManagerWrapper mPowerManagerWrapper;
|
||||
|
||||
int mRotationToReport = 0;
|
||||
boolean mKeyguardShowingAndNotOccluded = false;
|
||||
boolean mOkToAnimate = true;
|
||||
|
||||
private Runnable mRunnableWhenAddingSplashScreen;
|
||||
|
||||
TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier,
|
||||
PowerManagerWrapper powerManagerWrapper) {
|
||||
mWmSupplier = wmSupplier;
|
||||
mPowerManagerWrapper = powerManagerWrapper;
|
||||
TestWindowManagerPolicy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
|
||||
throws RemoteException {
|
||||
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,42 +73,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
|
||||
return attrs.type == TYPE_NOTIFICATION_SHADE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a runnable to run when adding a splash screen which gets executed after the window has
|
||||
* been added but before returning the surface.
|
||||
*/
|
||||
void setRunnableWhenAddingSplashScreen(Runnable r) {
|
||||
mRunnableWhenAddingSplashScreen = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
|
||||
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
|
||||
int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
|
||||
final com.android.server.wm.WindowState window;
|
||||
final ActivityRecord activity;
|
||||
final WindowManagerService wm = mWmSupplier.get();
|
||||
synchronized (wm.mGlobalLock) {
|
||||
activity = wm.mRoot.getActivityRecord(appToken);
|
||||
IWindow iWindow = mock(IWindow.class);
|
||||
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
|
||||
window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, activity,
|
||||
"Starting window", 0 /* ownerId */, 0 /* userId*/, false /* internalWindows */,
|
||||
wm, mock(Session.class), iWindow, mPowerManagerWrapper);
|
||||
activity.mStartingWindow = window;
|
||||
}
|
||||
if (mRunnableWhenAddingSplashScreen != null) {
|
||||
mRunnableWhenAddingSplashScreen.run();
|
||||
mRunnableWhenAddingSplashScreen = null;
|
||||
}
|
||||
return (a) -> {
|
||||
synchronized (wm.mGlobalLock) {
|
||||
activity.removeChild(window);
|
||||
activity.mStartingWindow = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animation createHiddenByKeyguardExit(boolean onWallpaper,
|
||||
boolean goingToNotificationShade, boolean subtleAnimation) {
|
||||
|
||||
@@ -54,7 +54,6 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER;
|
||||
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
|
||||
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
|
||||
@@ -1435,18 +1434,12 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
mAtm = service;
|
||||
mWMService = mAtm.mWindowManager;
|
||||
mPowerManagerWrapper = powerManagerWrapper;
|
||||
if (DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
|
||||
mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
|
||||
}
|
||||
mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
|
||||
mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
|
||||
}
|
||||
|
||||
void setRunnableWhenAddingSplashScreen(Runnable r) {
|
||||
if (DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
mRunnableWhenAddingSplashScreen = r;
|
||||
} else {
|
||||
((TestWindowManagerPolicy) mWMService.mPolicy).setRunnableWhenAddingSplashScreen(r);
|
||||
}
|
||||
mRunnableWhenAddingSplashScreen = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user