Loading core/java/android/app/ContextImpl.java +14 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; } Loading core/java/android/view/IWindowManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading core/java/android/window/WindowContext.java +56 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -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 Loading Loading @@ -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(); } } } core/java/android/window/flags/windowing_sdk.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -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 } } core/tests/coretests/src/android/window/WindowContextTest.java +21 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading
core/java/android/app/ContextImpl.java +14 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; } Loading
core/java/android/view/IWindowManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/window/WindowContext.java +56 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -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 Loading Loading @@ -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(); } } }
core/java/android/window/flags/windowing_sdk.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -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 } }
core/tests/coretests/src/android/window/WindowContextTest.java +21 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading