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

Commit 66615757 authored by Charles Chen's avatar Charles Chen
Browse files

Fix DecorView error about non-visual context

This error showed because DecorContext uses application context
to get WindowManager. This CL changes to use PhoneWindow to obtain
WindowManager instance. Also refactor ctr to obtain context from
PhoneWindow.

Bug: 152806048
Test: manual - enable strict mode and check the error log not shown.
Test: atest DecorContextTest
Test: atest MemoryTests#testActivityRecreation
Change-Id: I1d416b9cdb015c9bc3553571041f3b14bb9da5da
parent 4bf358fe
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;

import java.util.List;
@@ -69,7 +70,8 @@ import java.util.List;
public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    @VisibleForTesting
    public final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;
+39 −37
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@ import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.view.ContextThemeWrapper;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.contentcapture.ContentCaptureManager;

import com.android.internal.annotations.VisibleForTesting;
@@ -31,8 +29,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;

/**
 * Context for decor views which can be seeded with pure application context and not depend on the
 * activity, but still provide some of the facilities that Activity has,
 * Context for decor views which can be seeded with display context and not depend on the activity,
 * but still provide some of the facilities that Activity has,
 * e.g. themes, activity-based resources, etc.
 *
 * @hide
@@ -40,79 +38,83 @@ import java.lang.ref.WeakReference;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class DecorContext extends ContextThemeWrapper {
    private PhoneWindow mPhoneWindow;
    private WindowManager mWindowManager;
    private Resources mActivityResources;
    private Resources mResources;
    private ContentCaptureManager mContentCaptureManager;

    private WeakReference<Context> mActivityContext;
    private WeakReference<Context> mContext;

    // TODO(b/149928768): Non-activity context can be passed.
    @VisibleForTesting
    public DecorContext(Context context, Context activityContext) {
        super(context.createDisplayContext(activityContext.getDisplayNoVerify()), null);
        mActivityContext = new WeakReference<>(activityContext);
        mActivityResources = activityContext.getResources();
    public DecorContext(Context baseContext, PhoneWindow phoneWindow) {
        super(null /* base */, null);
        setPhoneWindow(phoneWindow);
        final Context displayContext = baseContext.createDisplayContext(
                // TODO(b/149790106): Non-activity context can be passed.
                phoneWindow.getContext().getDisplayNoVerify());
        attachBaseContext(displayContext);
    }

    void setPhoneWindow(PhoneWindow phoneWindow) {
        mPhoneWindow = phoneWindow;
        mWindowManager = null;
        final Context context = phoneWindow.getContext();
        mContext = new WeakReference<>(context);
        mResources = context.getResources();
    }

    @Override
    public Object getSystemService(String name) {
        if (Context.WINDOW_SERVICE.equals(name)) {
            if (mWindowManager == null) {
                WindowManagerImpl wm =
                        (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
                mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
            }
            return mWindowManager;
            return mPhoneWindow.getWindowManager();
        }
        final Context context = mContext.get();
        if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) {
            if (mContentCaptureManager == null) {
                Context activityContext = mActivityContext.get();
                if (activityContext != null) {
                    mContentCaptureManager = (ContentCaptureManager) activityContext
                            .getSystemService(name);
                }
            if (context != null && mContentCaptureManager == null) {
                mContentCaptureManager = (ContentCaptureManager) context.getSystemService(name);
            }
            return mContentCaptureManager;
        }
        // TODO(b/154191411): Try to revisit this issue in S.
        // We use application to get DisplayManager here because ViewRootImpl holds reference of
        // DisplayManager and implicitly holds reference of mContext, which makes activity cannot
        // be GC'd even after destroyed if mContext is an activity object.
        if (Context.DISPLAY_SERVICE.equals(name)) {
            return super.getSystemService(name);
        }
        // LayoutInflater and WallpaperManagerService should also be obtained from visual context
        // instead of base context.
        return (context != null) ? context.getSystemService(name) : super.getSystemService(name);
    }

    @Override
    public Resources getResources() {
        Context activityContext = mActivityContext.get();
        Context context = mContext.get();
        // Attempt to update the local cached Resources from the activity context. If the activity
        // is no longer around, return the old cached values.
        if (activityContext != null) {
            mActivityResources = activityContext.getResources();
        if (context != null) {
            mResources = context.getResources();
        }

        return mActivityResources;
        return mResources;
    }

    @Override
    public AssetManager getAssets() {
        return mActivityResources.getAssets();
        return mResources.getAssets();
    }

    @Override
    public AutofillOptions getAutofillOptions() {
        Context activityContext = mActivityContext.get();
        if (activityContext != null) {
            return activityContext.getAutofillOptions();
        Context context = mContext.get();
        if (context != null) {
            return context.getAutofillOptions();
        }
        return null;
    }

    @Override
    public ContentCaptureOptions getContentCaptureOptions() {
        Context activityContext = mActivityContext.get();
        if (activityContext != null) {
            return activityContext.getContentCaptureOptions();
        Context context = mContext.get();
        if (context != null) {
            return context.getContentCaptureOptions();
        }
        return null;
    }
+1 −1
Original line number Diff line number Diff line
@@ -2358,7 +2358,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                context = new DecorContext(applicationContext, this);
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
+34 −8
Original line number Diff line number Diff line
@@ -20,19 +20,24 @@ import static android.view.Display.DEFAULT_DISPLAY;

import static org.junit.Assert.assertEquals;

import android.app.Activity;
import android.app.EmptyActivity;
import android.content.Context;
import android.hardware.display.DisplayManagerGlobal;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import android.view.WindowManager;
import android.view.WindowManagerImpl;

import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;


import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -46,17 +51,22 @@ public final class DecorContextTest {
    private Context mContext;
    private static final int EXTERNAL_DISPLAY = DEFAULT_DISPLAY + 1;

    @Rule
    public ActivityTestRule<EmptyActivity> mActivityRule =
            new ActivityTestRule<>(EmptyActivity.class);

    @Before
    public void setUp() throws Exception {
        mContext = InstrumentationRegistry.getContext();
    public void setUp() {
        mContext = ApplicationProvider.getApplicationContext();
    }

    @Test
    public void testDecorContextWithDefaultDisplay() {
        Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), DEFAULT_DISPLAY,
                new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
        DecorContext context = new DecorContext(mContext.getApplicationContext(),
                mContext.createDisplayContext(defaultDisplay));
        final Context defaultDisplayContext = mContext.createDisplayContext(defaultDisplay);
        final PhoneWindow window = new PhoneWindow(defaultDisplayContext);
        DecorContext context = new DecorContext(mContext.getApplicationContext(), window);

        assertDecorContextDisplay(DEFAULT_DISPLAY, context);
    }
@@ -65,8 +75,9 @@ public final class DecorContextTest {
    public void testDecorContextWithExternalDisplay() {
        Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY,
                new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
        DecorContext context = new DecorContext(mContext.getApplicationContext(),
                mContext.createDisplayContext(display));
        final Context defaultDisplayContext = mContext.createDisplayContext(display);
        final PhoneWindow window = new PhoneWindow(defaultDisplayContext);
        DecorContext context = new DecorContext(mContext.getApplicationContext(), window);

        assertDecorContextDisplay(EXTERNAL_DISPLAY, context);
    }
@@ -76,4 +87,19 @@ public final class DecorContextTest {
        Display associatedDisplay = decorContext.getDisplay();
        assertEquals(expectedDisplayId, associatedDisplay.getDisplayId());
    }

    @Test
    public void testGetWindowManagerFromVisualDecorContext() throws Throwable {
        mActivityRule.runOnUiThread(() -> {
            Activity activity = mActivityRule.getActivity();
            final DecorContext decorContext = new DecorContext(mContext.getApplicationContext(),
                    (PhoneWindow) activity.getWindow());
            WindowManagerImpl actualWm = (WindowManagerImpl)
                    decorContext.getSystemService(WindowManager.class);
            WindowManagerImpl expectedWm = (WindowManagerImpl)
                    activity.getSystemService(WindowManager.class);
            // Verify that window manager is from activity not application context.
            assertEquals(expectedWm.mContext, actualWm.mContext);
        });
    }
}