Merge "Cleanup old SplashScreen code"

This commit is contained in:
Vadim Caen
2021-11-01 13:30:13 +00:00
committed by Android (Google) Code Review
16 changed files with 169 additions and 1503 deletions

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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.
*/

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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);

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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());
}
}

View File

@@ -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) {

View File

@@ -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