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

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

Add WindowContextCleaner

Thie CL adds a cleaner to clean up WindowContext vs overriding fianlize.
It give system server more time (5X original timeout) to detach
WindowContext.

Fixes: 339727951
Test: atest WindowContextTest
Flag: com.android.window.flags.clean_up_window_context_with_cleaner
Change-Id: I2ef4a7f1601aa16f8130a6965e84852b04aa283c
parent 1491f937
Loading
Loading
Loading
Loading
+14 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.StrictMode.vmIncorrectContextUseEnabled;
import static android.permission.flags.Flags.shouldRegisterAttributionSource;
import static android.view.WindowManager.LayoutParams.WindowType;
import static android.window.WindowContext.registerCleaner;

import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -3429,12 +3430,17 @@ class ContextImpl extends Context {
        // WindowContainer. We should detach from WindowContainer when the Context is finalized
        // if this Context is not a WindowContext. WindowContext finalization is handled in
        // WindowContext class.
        if (mToken instanceof WindowTokenClient && mOwnsToken) {
            WindowTokenClientController.getInstance().detachIfNeeded(
                    (WindowTokenClient) mToken);
        try {
            if (!(com.android.window.flags.Flags.cleanUpWindowContextWithCleaner()
                    || com.android.window.flags.Flags.trackSystemUiContextBeforeWms())
                    && mToken instanceof WindowTokenClient && mOwnsToken) {
                WindowTokenClientController.getInstance()
                        .detachIfNeeded((WindowTokenClient) mToken);
            }
        } finally {
            super.finalize();
        }
    }

    @UnsupportedAppUsage
    static ContextImpl createSystemContext(ActivityThread mainThread) {
@@ -3476,7 +3482,10 @@ class ContextImpl extends Context {
        WindowTokenClientController.getInstance().attachToDisplayContent(token, displayId);
        context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
        context.mOwnsToken = true;

        if (!com.android.window.flags.Flags.trackSystemUiContextBeforeWms()) {
            // #registerCleaner only support SystemUiContext or WindowContext.
            registerCleaner(systemUiContext);
        }
        return systemUiContext;
    }

+1 −1
Original line number Diff line number Diff line
@@ -999,7 +999,7 @@ interface IWindowManager
     *
     * @param clientToken the window context's token
     */
    void detachWindowContext(IBinder clientToken);
    oneway void detachWindowContext(IBinder clientToken);

    /**
     * Reparents the {@link android.window.WindowContext} to the
+56 −6
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ import com.android.window.flags.Flags;

import java.lang.ref.Reference;

import sun.misc.Cleaner;

/**
 * {@link WindowContext} is a context for non-activity windows such as
 * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} windows or system
@@ -92,9 +94,9 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
        mType = type;
        mOptions = options;
        mWindowManager = createWindowContextWindowManager(this);
        WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
        mController = new WindowContextController(requireNonNull(token));

        WindowTokenClient token = (WindowTokenClient) requireNonNull(getWindowContextToken());
        mController = new WindowContextController(token);
        registerCleaner(this);
        Reference.reachabilityFence(this);
    }

@@ -134,9 +136,14 @@ public class WindowContext extends ContextWrapper implements WindowProvider,

    @Override
    protected void finalize() throws Throwable {
        try {
            if (!Flags.cleanUpWindowContextWithCleaner()) {
                release();
            }
        } finally {
            super.finalize();
        }
    }

    /** Used for test to invoke because we can't invoke finalize directly. */
    @VisibleForTesting
@@ -287,4 +294,47 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
            return mDecorView;
        }
    }

    /**
     * Registers a {@link WindowContext} or a {@link SystemUiContext} with a cleaner.
     *
     * @throws IllegalArgumentException if the context is not a {@link WindowContext} or
     * {@link SystemUiContext}.
     */
    public static void registerCleaner(@NonNull Context context) {
        if (!Flags.cleanUpWindowContextWithCleaner()) {
            return;
        }

        if (!(context instanceof WindowContext) && !(context instanceof SystemUiContext)) {
            throw new IllegalArgumentException("The context must be either WindowContext or"
                    + " SystemUiContext.");
        }
        final ContextWrapper wrapper = (ContextWrapper) context;
        Cleaner.create(context, new WindowContextCleaner(wrapper));
    }

    /**
     * A {@link WindowContext} cleaner that applies when the {@link WindowContext} is going to be
     * garbage-collected.
     */
    private static class WindowContextCleaner implements Runnable {

        @NonNull
        private final Context mBaseContext;

        WindowContextCleaner(@NonNull ContextWrapper wrapper) {
            // Cache the base Context to prevent hold the reference of WindowContext. The cleaner
            // will work only if all strong references of WindowContext are gone.
            mBaseContext = requireNonNull(wrapper.getBaseContext());
        }

        @Override
        public void run() {
            final WindowTokenClient token =
                    (WindowTokenClient) requireNonNull(mBaseContext.getWindowContextToken());
            WindowTokenClientController.getInstance().detachIfNeeded(token);
            mBaseContext.destroy();
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -232,3 +232,14 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    namespace: "windowing_sdk"
    name: "clean_up_window_context_with_cleaner"
    description: "Using Cleaner to clean up WindowContext instead of overriding finalize"
    bug: "339727951"
    is_fixed_read_only: true
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+21 −0
Original line number Diff line number Diff line
@@ -53,9 +53,11 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.PollingCheck;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
@@ -71,6 +73,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;

import com.android.frameworks.coretests.R;
import com.android.internal.util.GcUtils;
import com.android.window.flags.Flags;

import org.junit.After;
@@ -515,6 +518,24 @@ public class WindowContextTest {
        return (WindowContext) instContext.createWindowContext(display, type,  null /* options */);
    }

    @Test
    public void testWindowContextCleanup() {
        final WindowContext windowContext = createWindowContext();

        windowContext.getSystemService(WindowManager.class).getCurrentWindowMetrics();

        GcUtils.runGcAndFinalizersSync();

        PollingCheck.waitFor(() -> {
            try {
                return !mWms.isWindowToken(windowContext.getWindowContextToken());
            } catch (RemoteException e) {
                fail("Fail due to " + e);
            }
            return false;
        });
    }

    private static class ConfigurationListener implements ComponentCallbacks {
        private Configuration mConfiguration;
        private CountDownLatch mLatch = new CountDownLatch(1);