Introduce WindowContext API
Test: atest WmTests CtsWindowManagerDeviceTestCases Bug: 128338354 Change-Id: I9c9dfc5e7f4edd4c968e60d2ffcbb19b5c72a853
This commit is contained in:
@@ -9940,6 +9940,7 @@ package android.content {
|
||||
method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
|
||||
method @NonNull public android.content.Context createFeatureContext(@Nullable String);
|
||||
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method @NonNull public android.content.Context createWindowContext(int);
|
||||
method public abstract String[] databaseList();
|
||||
method public abstract boolean deleteDatabase(String);
|
||||
method public abstract boolean deleteFile(String);
|
||||
@@ -9964,6 +9965,7 @@ package android.content {
|
||||
method public abstract java.io.File getDataDir();
|
||||
method public abstract java.io.File getDatabasePath(String);
|
||||
method public abstract java.io.File getDir(String, int);
|
||||
method @Nullable public android.view.Display getDisplay();
|
||||
method @Nullable public final android.graphics.drawable.Drawable getDrawable(@DrawableRes int);
|
||||
method @Nullable public abstract java.io.File getExternalCacheDir();
|
||||
method public abstract java.io.File[] getExternalCacheDirs();
|
||||
@@ -54146,7 +54148,7 @@ package android.view {
|
||||
}
|
||||
|
||||
public interface WindowManager extends android.view.ViewManager {
|
||||
method public android.view.Display getDefaultDisplay();
|
||||
method @Deprecated public android.view.Display getDefaultDisplay();
|
||||
method public void removeViewImmediate(android.view.View);
|
||||
}
|
||||
|
||||
|
||||
@@ -754,7 +754,6 @@ package android.content {
|
||||
method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
|
||||
method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method @NonNull public java.io.File getCrateDir(@NonNull String);
|
||||
method public abstract android.view.Display getDisplay();
|
||||
method public abstract int getDisplayId();
|
||||
method public android.os.UserHandle getUser();
|
||||
method public int getUserId();
|
||||
@@ -775,7 +774,6 @@ package android.content {
|
||||
}
|
||||
|
||||
public class ContextWrapper extends android.content.Context {
|
||||
method public android.view.Display getDisplay();
|
||||
method public int getDisplayId();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package android.app;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.AutofillOptions;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -201,7 +200,7 @@ class ContextImpl extends Context {
|
||||
@UnsupportedAppUsage
|
||||
private @Nullable ClassLoader mClassLoader;
|
||||
|
||||
private final @Nullable IBinder mActivityToken;
|
||||
private final @Nullable IBinder mToken;
|
||||
|
||||
private final @NonNull UserHandle mUser;
|
||||
|
||||
@@ -219,7 +218,7 @@ class ContextImpl extends Context {
|
||||
private final @NonNull ResourcesManager mResourcesManager;
|
||||
@UnsupportedAppUsage
|
||||
private @NonNull Resources mResources;
|
||||
private @Nullable Display mDisplay; // may be null if default display
|
||||
private @Nullable Display mDisplay; // may be null if invalid display or not initialized yet.
|
||||
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
|
||||
private final int mFlags;
|
||||
@@ -244,6 +243,9 @@ class ContextImpl extends Context {
|
||||
|
||||
private final Object mSync = new Object();
|
||||
|
||||
private boolean mIsSystemOrSystemUiContext;
|
||||
private boolean mIsUiContext;
|
||||
|
||||
@GuardedBy("mSync")
|
||||
private File mDatabasesDir;
|
||||
@GuardedBy("mSync")
|
||||
@@ -1883,6 +1885,9 @@ class ContextImpl extends Context {
|
||||
|
||||
@Override
|
||||
public Object getSystemService(String name) {
|
||||
if (isUiComponent(name) && !isUiContext()) {
|
||||
Log.w(TAG, name + " should be accessed from Activity or other visual Context");
|
||||
}
|
||||
return SystemServiceRegistry.getSystemService(this, name);
|
||||
}
|
||||
|
||||
@@ -1891,6 +1896,15 @@ class ContextImpl extends Context {
|
||||
return SystemServiceRegistry.getSystemServiceName(serviceClass);
|
||||
}
|
||||
|
||||
boolean isUiContext() {
|
||||
return mIsSystemOrSystemUiContext || mIsUiContext;
|
||||
}
|
||||
|
||||
private static boolean isUiComponent(String name) {
|
||||
return WINDOW_SERVICE.equals(name) || LAYOUT_INFLATER_SERVICE.equals(name)
|
||||
|| WALLPAPER_SERVICE.equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkPermission(String permission, int pid, int uid) {
|
||||
if (permission == null) {
|
||||
@@ -2229,12 +2243,12 @@ class ContextImpl extends Context {
|
||||
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
|
||||
flags | CONTEXT_REGISTER_PACKAGE);
|
||||
if (pi != null) {
|
||||
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mActivityToken,
|
||||
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mToken,
|
||||
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
|
||||
|
||||
final int displayId = getDisplayId();
|
||||
|
||||
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
|
||||
c.setResources(createResources(mToken, pi, null, displayId, null,
|
||||
getDisplayAdjustments(displayId).getCompatibilityInfo()));
|
||||
if (c.mResources != null) {
|
||||
return c;
|
||||
@@ -2258,18 +2272,18 @@ class ContextImpl extends Context {
|
||||
// The system resources are loaded in every application, so we can safely copy
|
||||
// the context without reloading Resources.
|
||||
return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null,
|
||||
mActivityToken, user, flags, null, null);
|
||||
mToken, user, flags, null, null);
|
||||
}
|
||||
|
||||
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
|
||||
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
|
||||
if (pi != null) {
|
||||
ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null,
|
||||
mActivityToken, user, flags, null, null);
|
||||
mToken, user, flags, null, null);
|
||||
|
||||
final int displayId = getDisplayId();
|
||||
|
||||
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
|
||||
c.setResources(createResources(mToken, pi, null, displayId, null,
|
||||
getDisplayAdjustments(displayId).getCompatibilityInfo()));
|
||||
if (c.mResources != null) {
|
||||
return c;
|
||||
@@ -2301,12 +2315,12 @@ class ContextImpl extends Context {
|
||||
final String[] paths = mPackageInfo.getSplitPaths(splitName);
|
||||
|
||||
final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
|
||||
mFeatureId, splitName, mActivityToken, mUser, mFlags, classLoader, null);
|
||||
mFeatureId, splitName, mToken, mUser, mFlags, classLoader, null);
|
||||
|
||||
final int displayId = getDisplayId();
|
||||
|
||||
context.setResources(ResourcesManager.getInstance().getResources(
|
||||
mActivityToken,
|
||||
mToken,
|
||||
mPackageInfo.getResDir(),
|
||||
paths,
|
||||
mPackageInfo.getOverlayDirs(),
|
||||
@@ -2325,10 +2339,10 @@ class ContextImpl extends Context {
|
||||
}
|
||||
|
||||
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
|
||||
mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null);
|
||||
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
|
||||
|
||||
final int displayId = getDisplayId();
|
||||
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
|
||||
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
|
||||
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
|
||||
return context;
|
||||
}
|
||||
@@ -2340,19 +2354,36 @@ class ContextImpl extends Context {
|
||||
}
|
||||
|
||||
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
|
||||
mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null);
|
||||
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
|
||||
|
||||
final int displayId = display.getDisplayId();
|
||||
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
|
||||
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
|
||||
null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
|
||||
context.mDisplay = display;
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull WindowContext createWindowContext(int type) {
|
||||
if (getDisplay() == null) {
|
||||
throw new UnsupportedOperationException("WindowContext can only be created from "
|
||||
+ "other visual contexts, such as Activity or one created with "
|
||||
+ "Context#createDisplayContext(Display)");
|
||||
}
|
||||
return new WindowContext(this, null /* token */, type);
|
||||
}
|
||||
|
||||
ContextImpl createBaseWindowContext(IBinder token) {
|
||||
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
|
||||
mSplitName, token, mUser, mFlags, mClassLoader, null);
|
||||
context.mIsUiContext = true;
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Context createFeatureContext(@Nullable String featureId) {
|
||||
return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
|
||||
mActivityToken, mUser, mFlags, mClassLoader, null);
|
||||
mToken, mUser, mFlags, mClassLoader, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2360,7 +2391,7 @@ class ContextImpl extends Context {
|
||||
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
|
||||
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
|
||||
return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
|
||||
mActivityToken, mUser, flags, mClassLoader, null);
|
||||
mToken, mUser, flags, mClassLoader, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2368,7 +2399,7 @@ class ContextImpl extends Context {
|
||||
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
|
||||
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
|
||||
return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
|
||||
mActivityToken, mUser, flags, mClassLoader, null);
|
||||
mToken, mUser, flags, mClassLoader, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2394,8 +2425,6 @@ class ContextImpl extends Context {
|
||||
return (mFlags & Context.CONTEXT_IGNORE_SECURITY) != 0;
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@TestApi
|
||||
@Override
|
||||
public Display getDisplay() {
|
||||
if (mDisplay == null) {
|
||||
@@ -2408,7 +2437,8 @@ class ContextImpl extends Context {
|
||||
|
||||
@Override
|
||||
public int getDisplayId() {
|
||||
return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
|
||||
final Display display = getDisplay();
|
||||
return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2518,6 +2548,7 @@ class ContextImpl extends Context {
|
||||
context.setResources(packageInfo.getResources());
|
||||
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
|
||||
context.mResourcesManager.getDisplayMetrics());
|
||||
context.mIsSystemOrSystemUiContext = true;
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -2535,6 +2566,7 @@ class ContextImpl extends Context {
|
||||
context.setResources(createResources(null, packageInfo, null, displayId, null,
|
||||
packageInfo.getCompatibilityInfo()));
|
||||
context.updateDisplay(displayId);
|
||||
context.mIsSystemOrSystemUiContext = true;
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -2584,6 +2616,7 @@ class ContextImpl extends Context {
|
||||
|
||||
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
|
||||
activityInfo.splitName, activityToken, null, 0, classLoader, null);
|
||||
context.mIsUiContext = true;
|
||||
|
||||
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
|
||||
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
|
||||
@@ -2629,7 +2662,7 @@ class ContextImpl extends Context {
|
||||
}
|
||||
|
||||
mMainThread = mainThread;
|
||||
mActivityToken = activityToken;
|
||||
mToken = activityToken;
|
||||
mFlags = flags;
|
||||
|
||||
if (user == null) {
|
||||
@@ -2649,6 +2682,7 @@ class ContextImpl extends Context {
|
||||
opPackageName = container.mOpPackageName;
|
||||
setResources(container.mResources);
|
||||
mDisplay = container.mDisplay;
|
||||
mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext;
|
||||
} else {
|
||||
mBasePackageName = packageInfo.mPackageName;
|
||||
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
|
||||
@@ -2710,7 +2744,7 @@ class ContextImpl extends Context {
|
||||
@Override
|
||||
@UnsupportedAppUsage
|
||||
public IBinder getActivityToken() {
|
||||
return mActivityToken;
|
||||
return mToken;
|
||||
}
|
||||
|
||||
private void checkMode(int mode) {
|
||||
|
||||
123
core/java/android/app/WindowContext.java
Normal file
123
core/java/android/app/WindowContext.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.app;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.view.WindowManagerImpl;
|
||||
|
||||
/**
|
||||
* {@link WindowContext} is a context for non-activity windows such as
|
||||
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} windows or system
|
||||
* windows. Its resources and configuration are adjusted to the area of the display that will be
|
||||
* used when a new window is added via {@link android.view.WindowManager.addView}.
|
||||
*
|
||||
* @see Context#createWindowContext(int)
|
||||
* @hide
|
||||
*/
|
||||
// TODO(b/128338354): Handle config/display changes from server side.
|
||||
public class WindowContext extends ContextWrapper {
|
||||
private final WindowManagerImpl mWindowManager;
|
||||
private final IWindowManager mWms;
|
||||
private final IBinder mToken;
|
||||
private final int mDisplayId;
|
||||
private boolean mOwnsToken;
|
||||
|
||||
/**
|
||||
* Default constructor. Can either accept an existing token or generate one and registers it
|
||||
* with the server if necessary.
|
||||
*
|
||||
* @param base Base {@link Context} for this new instance.
|
||||
* @param token A valid {@link com.android.server.wm.WindowToken}. Pass {@code null} to generate
|
||||
* one.
|
||||
* @param type Window type to be used with this context.
|
||||
* @hide
|
||||
*/
|
||||
public WindowContext(Context base, IBinder token, int type) {
|
||||
super(null /* base */);
|
||||
|
||||
mWms = WindowManagerGlobal.getWindowManagerService();
|
||||
if (token != null && !isWindowToken(token)) {
|
||||
throw new IllegalArgumentException("Token must be registered to server.");
|
||||
}
|
||||
|
||||
final ContextImpl contextImpl = createBaseWindowContext(base, token);
|
||||
attachBaseContext(contextImpl);
|
||||
contextImpl.setOuterContext(this);
|
||||
|
||||
mToken = token != null ? token : new Binder();
|
||||
mDisplayId = getDisplayId();
|
||||
mWindowManager = new WindowManagerImpl(this);
|
||||
mWindowManager.setDefaultToken(mToken);
|
||||
|
||||
// TODO(b/128338354): Obtain the correct config from WM and adjust resources.
|
||||
if (token != null) {
|
||||
mOwnsToken = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mWms.addWindowContextToken(mToken, type, mDisplayId, getPackageName());
|
||||
// TODO(window-context): remove token with a DeathObserver
|
||||
} catch (RemoteException e) {
|
||||
mOwnsToken = false;
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mOwnsToken = true;
|
||||
}
|
||||
|
||||
/** Check if the passed window token is registered with the server. */
|
||||
private boolean isWindowToken(@NonNull IBinder token) {
|
||||
try {
|
||||
return mWms.isWindowToken(token);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ContextImpl createBaseWindowContext(Context outer, IBinder token) {
|
||||
final ContextImpl contextImpl = ContextImpl.getImpl(outer);
|
||||
return contextImpl.createBaseWindowContext(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSystemService(String name) {
|
||||
if (WINDOW_SERVICE.equals(name)) {
|
||||
return mWindowManager;
|
||||
}
|
||||
return super.getSystemService(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (mOwnsToken) {
|
||||
try {
|
||||
mWms.removeWindowToken(mToken, mDisplayId);
|
||||
mOwnsToken = false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
@@ -5719,6 +5719,63 @@ public abstract class Context {
|
||||
*/
|
||||
public abstract Context createDisplayContext(@NonNull Display display);
|
||||
|
||||
/**
|
||||
* Creates a Context for a non-activity window.
|
||||
*
|
||||
* <p>
|
||||
* A window context is a context that can be used to add non-activity windows, such as
|
||||
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}. A window context
|
||||
* must be created from a context that has an associated {@link Display}, such as
|
||||
* {@link android.app.Activity Activity} or a context created with
|
||||
* {@link #createDisplayContext(Display)}.
|
||||
*
|
||||
* <p>
|
||||
* The window context is created with the appropriate {@link Configuration} for the area of the
|
||||
* display that the windows created with it can occupy; it must be used when
|
||||
* {@link android.view.LayoutInflater inflating} views, such that they can be inflated with
|
||||
* proper {@link Resources}.
|
||||
*
|
||||
* Below is a sample code to <b>add an application overlay window on the primary display:<b/>
|
||||
* <pre class="prettyprint">
|
||||
* ...
|
||||
* final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
|
||||
* final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
|
||||
* final Context windowContext = anyContext.createDisplayContext(primaryDisplay)
|
||||
* .createWindowContext(TYPE_APPLICATION_OVERLAY);
|
||||
* final View overlayView = Inflater.from(windowContext).inflate(someLayoutXml, null);
|
||||
*
|
||||
* // WindowManager.LayoutParams initialization
|
||||
* ...
|
||||
* mParams.type = TYPE_APPLICATION_OVERLAY;
|
||||
* ...
|
||||
*
|
||||
* mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This context's configuration and resources are adjusted to a display area where the windows
|
||||
* with provided type will be added. <b>Note that all windows associated with the same context
|
||||
* will have an affinity and can only be moved together between different displays or areas on a
|
||||
* display.</b> If there is a need to add different window types, or non-associated windows,
|
||||
* separate Contexts should be used.
|
||||
* </p>
|
||||
*
|
||||
* @param type Window type in {@link WindowManager.LayoutParams}
|
||||
* @return A {@link Context} that can be used to create windows.
|
||||
* @throws UnsupportedOperationException if this is called on a non-UI context, such as
|
||||
* {@link android.app.Application Application} or {@link android.app.Service Service}.
|
||||
*
|
||||
* @see #getSystemService(String)
|
||||
* @see #getSystemService(Class)
|
||||
* @see #WINDOW_SERVICE
|
||||
* @see #LAYOUT_INFLATER_SERVICE
|
||||
* @see #WALLPAPER_SERVICE
|
||||
* @throws IllegalArgumentException if token is invalid
|
||||
*/
|
||||
public @NonNull Context createWindowContext(int type) {
|
||||
throw new RuntimeException("Not implemented. Must override in a subclass.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new Context object for the current Context but for a different feature in the app.
|
||||
* Features can be used by complex apps to separate logical parts.
|
||||
@@ -5803,17 +5860,22 @@ public abstract class Context {
|
||||
public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
|
||||
|
||||
/**
|
||||
* Get the display this context is associated with. Applications should use this method with
|
||||
* {@link android.app.Activity} or a context associated with a {@link Display} via
|
||||
* {@link #createDisplayContext(Display)} to get a display object associated with a Context, or
|
||||
* {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
|
||||
* @return Returns the {@link Display} object this context is associated with.
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
@TestApi
|
||||
public abstract Display getDisplay();
|
||||
@Nullable
|
||||
public Display getDisplay() {
|
||||
throw new RuntimeException("Not implemented. Must override in a subclass.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display ID.
|
||||
* Gets the ID of the display this context is associated with.
|
||||
*
|
||||
* @return display ID associated with this {@link Context}.
|
||||
* @see #getDisplay()
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
|
||||
@@ -976,6 +976,12 @@ public class ContextWrapper extends Context {
|
||||
return mBase.createDisplayContext(display);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Context createWindowContext(int type) {
|
||||
return mBase.createWindowContext(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Context createFeatureContext(@Nullable String featureId) {
|
||||
return mBase.createFeatureContext(featureId);
|
||||
@@ -992,11 +998,8 @@ public class ContextWrapper extends Context {
|
||||
return mBase.getDisplayAdjustments(displayId);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@UnsupportedAppUsage
|
||||
@TestApi
|
||||
@Override
|
||||
public Display getDisplay() {
|
||||
public @Nullable Display getDisplay() {
|
||||
return mBase.getDisplay();
|
||||
}
|
||||
|
||||
|
||||
@@ -423,10 +423,14 @@ public final class Display {
|
||||
/**
|
||||
* Internal method to create a display.
|
||||
* The display created with this method will have a static {@link DisplayAdjustments} applied.
|
||||
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
|
||||
* or {@link android.hardware.display.DisplayManager#getDisplay}
|
||||
* to get a display object.
|
||||
* Applications should use {@link android.content.Context#getDisplay} with
|
||||
* {@link android.app.Activity} or a context associated with a {@link Display} via
|
||||
* {@link android.content.Context#createDisplayContext(Display)}
|
||||
* to get a display object associated with a {@link android.app.Context}, or
|
||||
* {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
|
||||
*
|
||||
* @see android.content.Context#getDisplay()
|
||||
* @see android.content.Context#createDisplayContext(Display)
|
||||
* @hide
|
||||
*/
|
||||
public Display(DisplayManagerGlobal global, int displayId, /*@NotNull*/ DisplayInfo displayInfo,
|
||||
|
||||
@@ -111,6 +111,20 @@ interface IWindowManager
|
||||
|
||||
// These can only be called when holding the MANAGE_APP_TOKENS permission.
|
||||
void setEventDispatching(boolean enabled);
|
||||
|
||||
/** @return {@code true} if this binder is a registered window token. */
|
||||
boolean isWindowToken(in IBinder binder);
|
||||
/**
|
||||
* Adds window token for a given type.
|
||||
*
|
||||
* @param token Token to be registered.
|
||||
* @param type Window type to be used with this token.
|
||||
* @param displayId The ID of the display where this token should be added.
|
||||
* @param packageName The name of package to request to add window token.
|
||||
* @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
|
||||
* otherwise.
|
||||
*/
|
||||
int addWindowContextToken(IBinder token, int type, int displayId, String packageName);
|
||||
void addWindowToken(IBinder token, int type, int displayId);
|
||||
void removeWindowToken(IBinder token, int displayId);
|
||||
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
|
||||
|
||||
@@ -421,7 +421,9 @@ public interface WindowManager extends ViewManager {
|
||||
* </p>
|
||||
*
|
||||
* @return The display that this window manager is managing.
|
||||
* @deprecated Use {@link Context#getDisplay()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public Display getDefaultDisplay();
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,6 +71,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
|
||||
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
|
||||
import static android.view.WindowManagerGlobal.ADD_OKAY;
|
||||
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
|
||||
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
|
||||
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
|
||||
@@ -1367,52 +1368,10 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
boolean addToastWindowRequiresToken = false;
|
||||
|
||||
if (token == null) {
|
||||
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
|
||||
rootType, attrs.token, attrs.packageName)) {
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (rootType == TYPE_INPUT_METHOD) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (rootType == TYPE_VOICE_INTERACTION) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"Attempted to add voice interaction window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (rootType == TYPE_WALLPAPER) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (rootType == TYPE_DREAM) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (rootType == TYPE_QS_DIALOG) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"Attempted to add Accessibility overlay window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
if (type == TYPE_TOAST) {
|
||||
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
|
||||
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
|
||||
parentWindow)) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
|
||||
+ "%s. Aborting.", attrs.token);
|
||||
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
|
||||
}
|
||||
}
|
||||
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
|
||||
final boolean isRoundedCornerOverlay =
|
||||
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
|
||||
@@ -1697,6 +1656,56 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow,
|
||||
int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) {
|
||||
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (rootType == TYPE_INPUT_METHOD) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (rootType == TYPE_VOICE_INTERACTION) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"Attempted to add voice interaction window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (rootType == TYPE_WALLPAPER) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (rootType == TYPE_DREAM) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (rootType == TYPE_QS_DIALOG) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"Attempted to add Accessibility overlay window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
if (type == TYPE_TOAST) {
|
||||
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
|
||||
if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) {
|
||||
ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
|
||||
+ "%s. Aborting.", tokenForLog);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing {@link DisplayContent} or create a new one if the display is registered in
|
||||
* DisplayManager.
|
||||
@@ -2501,16 +2510,36 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
@Override
|
||||
public void addWindowToken(IBinder binder, int type, int displayId) {
|
||||
if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
|
||||
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
|
||||
addWindowContextToken(binder, type, displayId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addWindowContextToken(IBinder binder, int type, int displayId, String packageName) {
|
||||
final boolean callerCanManageAppTokens =
|
||||
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
|
||||
if (!callerCanManageAppTokens) {
|
||||
// TODO(window-context): refactor checkAddPermission to not take attrs.
|
||||
LayoutParams attrs = new LayoutParams(type);
|
||||
attrs.packageName = packageName;
|
||||
final int res = mPolicy.checkAddPermission(attrs, new int[1]);
|
||||
if (res != ADD_OKAY) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mGlobalLock) {
|
||||
if (!callerCanManageAppTokens) {
|
||||
if (!unprivilegedAppCanCreateTokenWith(null, Binder.getCallingUid(), type, type,
|
||||
null, packageName) || packageName == null) {
|
||||
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
|
||||
}
|
||||
}
|
||||
|
||||
final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
|
||||
if (dc == null) {
|
||||
ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
|
||||
+ " for non-exiting displayId=%d", binder, displayId);
|
||||
return;
|
||||
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
|
||||
}
|
||||
|
||||
WindowToken token = dc.getWindowToken(binder);
|
||||
@@ -2518,15 +2547,28 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
|
||||
+ " for already created window token: %s"
|
||||
+ " displayId=%d", binder, token, displayId);
|
||||
return;
|
||||
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
|
||||
}
|
||||
// TODO(window-container): Clean up dead tokens
|
||||
if (type == TYPE_WALLPAPER) {
|
||||
new WallpaperWindowToken(this, binder, true, dc,
|
||||
true /* ownerCanManageAppTokens */);
|
||||
new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
|
||||
} else {
|
||||
new WindowToken(this, binder, type, true, dc, true /* ownerCanManageAppTokens */);
|
||||
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens);
|
||||
}
|
||||
}
|
||||
return WindowManagerGlobal.ADD_OKAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWindowToken(IBinder binder) {
|
||||
synchronized (mGlobalLock) {
|
||||
final WindowToken windowToken = mRoot.getWindowToken(binder);
|
||||
if (windowToken == null) {
|
||||
return false;
|
||||
}
|
||||
// We don't allow activity tokens in WindowContext. TODO(window-context): rename method
|
||||
return windowToken.asActivityRecord() == null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package android.test.mock {
|
||||
|
||||
public class MockContext extends android.content.Context {
|
||||
method public android.view.Display getDisplay();
|
||||
method public int getDisplayId();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.test.mock;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
@@ -810,6 +811,11 @@ public class MockContext extends Context {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Context createWindowContext(int type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRestricted() {
|
||||
throw new UnsupportedOperationException();
|
||||
@@ -821,7 +827,6 @@ public class MockContext extends Context {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public Display getDisplay() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
Reference in New Issue
Block a user