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

Commit 02c0e4d2 authored by Andrii Kulian's avatar Andrii Kulian Committed by Charles Chen
Browse files

Handle config/display changes for WindowContext

Introduce IWindowToken to report config/display changes from
server side. When config change callback is received,
it will update the resources associated with the window token.

Test: WindowContextTests
Bug: 128338354
Bug: 146820733

Change-Id: I871bd78a21dbde1286786e65c340b6259b873660
parent 80c2e07e
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -2409,17 +2409,35 @@ class ContextImpl extends Context {
                    + "other visual contexts, such as Activity or one created with "
                    + "Context#createDisplayContext(Display)");
        }
        return new WindowContext(this, null /* token */, type, options);
        return new WindowContext(this, type, options);
    }

    ContextImpl createBaseWindowContext(IBinder token) {
        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
                mSplitName, token, mUser, mFlags, mClassLoader, null);
        context.mIsUiContext = true;

        context.mIsAssociatedWithDisplay = true;
        return context;
    }

    Resources createWindowContextResources() {
        final String resDir = mPackageInfo.getResDir();
        final String[] splitResDirs = mPackageInfo.getSplitResDirs();
        final String[] overlayDirs = mPackageInfo.getOverlayDirs();
        final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles;
        final int displayId = getDisplayId();
        final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
                ? mPackageInfo.getCompatibilityInfo()
                : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
        final List<ResourcesLoader> loaders = mResources.getLoaders();

        // TODO(b/128338354): Rename to createTokenResources
        return mResourcesManager.createBaseActivityResources(mToken, resDir, splitResDirs,
                overlayDirs, libDirs, displayId, null /* overrideConfig */,
                compatInfo, mClassLoader, loaders);
    }

    @Override
    public @NonNull Context createFeatureContext(@Nullable String featureId) {
        return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
+33 −0
Original line number Diff line number Diff line
/*
 ** Copyright 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.content.res.Configuration;
import android.view.IWindow;

/**
 * Callback to receive configuration changes from {@link com.android.server.WindowToken}.
 * WindowToken can be regarded to as a group of {@link android.view.IWindow} added from the same
 * visual context, such as {@link Activity} or one created with
 * {@link android.content.Context#createWindowContext(int)}. When WindowToken receives configuration
 * changes and/or when it is moved between displays, it will propagate the changes to client side
 * via this interface.
 * @see android.content.Context#createWindowContext(int)
 * {@hide}
 */
oneway interface IWindowToken {
    void onConfigurationChanged(in Configuration newConfig, int newDisplayId);
}
+24 −33
Original line number Diff line number Diff line
@@ -15,10 +15,12 @@
 */
package android.app;

import static android.view.WindowManagerGlobal.ADD_OKAY;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -26,73 +28,62 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;

import java.lang.ref.Reference;

/**
 * {@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}.
 * used when a new window is added via {@link android.view.WindowManager#addView}.
 *
 * @see Context#createWindowContext(int, Bundle)
 * @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 final WindowTokenClient mToken;
    private boolean mOwnsToken;

    /**
     * Default constructor. Can either accept an existing token or generate one and registers it
     * with the server if necessary.
     * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
     * the token.
     *
     * @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, Bundle options) {
    public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
        // Correct base context will be built once the token is resolved, so passing 'null' here.
        super(null /* base */);

        mWms = WindowManagerGlobal.getWindowManagerService();
        if (token != null && !isWindowToken(token)) {
            throw new IllegalArgumentException("Token must be registered to server.");
        }
        mToken = token != null ? token : new Binder();
        mToken = new WindowTokenClient();


        final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
        attachBaseContext(contextImpl);
        contextImpl.setOuterContext(this);

        mDisplayId = getDisplayId();
        mToken.attachContext(this);

        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;
        }
        int result;
        try {
            mWms.addWindowTokenWithOptions(mToken, type, mDisplayId, options, getPackageName());
            // Register the token with WindowManager. This will also call back with the current
            // config back to the client.
            result = mWms.addWindowTokenWithOptions(
                    mToken, type, getDisplayId(), options, 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;
        mOwnsToken = result == ADD_OKAY;
        Reference.reachabilityFence(this);
    }

    private static ContextImpl createBaseWindowContext(Context outer, IBinder token) {
@@ -112,7 +103,7 @@ public class WindowContext extends ContextWrapper {
    protected void finalize() throws Throwable {
        if (mOwnsToken) {
            try {
                mWms.removeWindowToken(mToken, mDisplayId);
                mWms.removeWindowToken(mToken, getDisplayId());
                mOwnsToken = false;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
+76 −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.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;

/**
 * Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from
 * server when window token config is updated or when it is moved between displays, and update the
 * resources associated with this token on the client side. This will make sure that
 * {@link WindowContext} instances will have updated resources and configuration.
 * @hide
 */
public class WindowTokenClient extends IWindowToken.Stub {
    /**
     * Attached {@link Context} for this window token to update configuration and resources.
     * Initialized by {@link #attachContext(Context)}.
     */
    private Context mContext = null;

    private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();

    /**
     * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
     * can only attach one {@link Context}.
     * <p>This method must be called before invoking
     * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle,
     * String)}.<p/>
     *
     * @param context context to be attached
     * @throws IllegalStateException if attached context has already existed.
     */
    void attachContext(@NonNull Context context) {
        if (mContext != null) {
            throw new IllegalStateException("Context is already attached.");
        }
        mContext = context;
        ContextImpl impl = ContextImpl.getImpl(mContext);
        impl.setResources(impl.createWindowContextResources());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
        final int currentDisplayId = mContext.getDisplayId();
        final boolean displayChanged = newDisplayId != currentDisplayId;
        final Configuration config = new Configuration(mContext.getResources()
                .getConfiguration());
        final boolean configChanged = config.isOtherSeqNewer(newConfig)
                && config.updateFrom(newConfig) != 0;
        if (displayChanged || configChanged) {
            // TODO(ag/9789103): update resource manager logic to track non-activity tokens
            mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId,
                    displayChanged);
        }
        if (displayChanged) {
            mContext.updateDisplay(newDisplayId);
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -458,7 +458,7 @@ public interface WindowManager extends ViewManager {
    }

    /**
     * Returns the largets {@link WindowMetrics} an app may expect in the current system state.
     * Returns the largest {@link WindowMetrics} an app may expect in the current system state.
     * <p>
     * The metrics describe the size of the largest potential area the window might occupy with
     * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
Loading