Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a316973 authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Introduce WindowContext API

Test: atest WmTests CtsWindowManagerDeviceTestCases
Bug: 128338354
Change-Id: I9c9dfc5e7f4edd4c968e60d2ffcbb19b5c72a853
parent 2175e3eb
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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);
  }
+0 −2
Original line number Diff line number Diff line
@@ -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();
  }

+56 −22
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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();
    }
}
+67 −5
Original line number Diff line number Diff line
@@ -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
Loading