diff options
| author | 2025-02-07 21:44:10 +0000 | |
|---|---|---|
| committer | 2025-02-08 17:51:21 +0000 | |
| commit | a32517c0a1924468d5ace0c9ff9fc6b0c31a2e64 (patch) | |
| tree | 163b2178a2c13e255118aa7c1ebd2233ab70dc41 | |
| parent | b088b1268362a33e6a92b7858d12286aa8cb9bf5 (diff) | |
Allow system organizer to watch additional config changes
System organizer may want to get TaskFragment info callbacks for some
configuration changes, e.g. Launcher wants UI mode change callbacks for
the TaskFragment.
Bug: 385676993
Test: WindowOrganizerTests TaskFragmentOrganizerControllerTest
Flag: EXEMPT bugfix
Change-Id: I493b1d908b89422cc55087da9e2319b366f6006a
5 files changed, 109 insertions, 4 deletions
diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java index 89327fe358f5..bc5ad50483ee 100644 --- a/core/java/android/window/TaskFragmentCreationParams.java +++ b/core/java/android/window/TaskFragmentCreationParams.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.TestApi; +import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; import android.graphics.Rect; import android.os.IBinder; @@ -112,12 +113,21 @@ public final class TaskFragmentCreationParams implements Parcelable { */ private final @ScreenOrientation int mOverrideOrientation; + /** + * {@link android.content.pm.ActivityInfo.Config} mask that specifies which + * configuration changes should trigger TaskFragment info change callbacks. + * + * @see android.content.pm.ActivityInfo.Config + */ + private final @ActivityInfo.Config int mConfigurationChangeMask; + private TaskFragmentCreationParams( @NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect initialRelativeBounds, @WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken, @Nullable IBinder pairedActivityToken, boolean allowTransitionWhenEmpty, - @ScreenOrientation int overrideOrientation) { + @ScreenOrientation int overrideOrientation, + @ActivityInfo.Config int configurationChangeMask) { if (pairedPrimaryFragmentToken != null && pairedActivityToken != null) { throw new IllegalArgumentException("pairedPrimaryFragmentToken and" + " pairedActivityToken should not be set at the same time."); @@ -131,6 +141,7 @@ public final class TaskFragmentCreationParams implements Parcelable { mPairedActivityToken = pairedActivityToken; mAllowTransitionWhenEmpty = allowTransitionWhenEmpty; mOverrideOrientation = overrideOrientation; + mConfigurationChangeMask = configurationChangeMask; } @NonNull @@ -186,6 +197,11 @@ public final class TaskFragmentCreationParams implements Parcelable { return mOverrideOrientation; } + /** @hide */ + public @ActivityInfo.Config int getConfigurationChangeMask() { + return mConfigurationChangeMask; + } + private TaskFragmentCreationParams(Parcel in) { mOrganizer = TaskFragmentOrganizerToken.CREATOR.createFromParcel(in); mFragmentToken = in.readStrongBinder(); @@ -196,6 +212,7 @@ public final class TaskFragmentCreationParams implements Parcelable { mPairedActivityToken = in.readStrongBinder(); mAllowTransitionWhenEmpty = in.readBoolean(); mOverrideOrientation = in.readInt(); + mConfigurationChangeMask = in.readInt(); } /** @hide */ @@ -210,6 +227,7 @@ public final class TaskFragmentCreationParams implements Parcelable { dest.writeStrongBinder(mPairedActivityToken); dest.writeBoolean(mAllowTransitionWhenEmpty); dest.writeInt(mOverrideOrientation); + dest.writeInt(mConfigurationChangeMask); } @NonNull @@ -238,6 +256,7 @@ public final class TaskFragmentCreationParams implements Parcelable { + " pairedActivityToken=" + mPairedActivityToken + " allowTransitionWhenEmpty=" + mAllowTransitionWhenEmpty + " overrideOrientation=" + mOverrideOrientation + + " configurationChangeMask=" + mConfigurationChangeMask + "}"; } @@ -275,6 +294,8 @@ public final class TaskFragmentCreationParams implements Parcelable { private @ScreenOrientation int mOverrideOrientation = SCREEN_ORIENTATION_UNSPECIFIED; + private @ActivityInfo.Config int mConfigurationChangeMask = 0; + public Builder(@NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) { mOrganizer = organizer; @@ -369,12 +390,30 @@ public final class TaskFragmentCreationParams implements Parcelable { return this; } + /** + * Sets {@link android.content.pm.ActivityInfo.Config} mask that specifies which + * configuration changes should trigger TaskFragment info change callbacks. + * + * Only system organizers are allowed to configure this value. This value is ignored for + * non-system organizers. + * + * @see android.content.pm.ActivityInfo.Config + * @hide + */ + @NonNull + public Builder setConfigurationChangeMask( + @ActivityInfo.Config int configurationChangeMask) { + mConfigurationChangeMask = configurationChangeMask; + return this; + } + /** Constructs the options to create TaskFragment with. */ @NonNull public TaskFragmentCreationParams build() { return new TaskFragmentCreationParams(mOrganizer, mFragmentToken, mOwnerToken, mInitialRelativeBounds, mWindowingMode, mPairedPrimaryFragmentToken, - mPairedActivityToken, mAllowTransitionWhenEmpty, mOverrideOrientation); + mPairedActivityToken, mAllowTransitionWhenEmpty, mOverrideOrientation, + mConfigurationChangeMask); } } } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index ba48fdc963de..129525729f83 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -394,6 +394,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ private boolean mAllowTransitionWhenEmpty; + /** + * Specifies which configuration changes should trigger TaskFragment info changed callbacks. + * Only system TaskFragment organizers are allowed to set this value. + */ + private @ActivityInfo.Config int mConfigurationChangeMaskForOrganizer; + /** When set, will force the task to report as invisible. */ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1; static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1; @@ -656,6 +662,17 @@ class TaskFragment extends WindowContainer<WindowContainer> { mAllowTransitionWhenEmpty = allowTransitionWhenEmpty; } + void setConfigurationChangeMaskForOrganizer(@ActivityInfo.Config int mask) { + // Only system organizers are allowed to set configuration change mask. + if (mTaskFragmentOrganizerController.isSystemOrganizer(mTaskFragmentOrganizer.asBinder())) { + mConfigurationChangeMaskForOrganizer = mask; + } + } + + @ActivityInfo.Config int getConfigurationChangeMaskForOrganizer() { + return mConfigurationChangeMaskForOrganizer; + } + /** @see #mIsolatedNav */ boolean isIsolatedNav() { return isEmbedded() && mIsolatedNav; diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index aaae160084a9..aa1baabb863e 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -349,8 +349,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr // Check if the info is different from the last reported info. final TaskFragmentInfo info = tf.getTaskFragmentInfo(); final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf); - if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer( - info.getConfiguration(), lastInfo.getConfiguration())) { + final int configurationChangeMask = tf.getConfigurationChangeMaskForOrganizer(); + if (info.equalsForTaskFragmentOrganizer(lastInfo) + && configurationsAreEqualForOrganizer(info.getConfiguration(), + lastInfo.getConfiguration(), configurationChangeMask)) { return null; } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index a11f4b1f3fc3..f411aaea97be 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -2441,10 +2441,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub /** Whether the configuration changes are important to report back to an organizer. */ static boolean configurationsAreEqualForOrganizer( Configuration newConfig, @Nullable Configuration oldConfig) { + return configurationsAreEqualForOrganizer(newConfig, oldConfig, 0 /* additionalMask */); + } + + /** + * Whether the configuration changes are important to report back to an organizer. + * + * @param newConfig the new configuration + * @param oldConfig the old configuration + * @param additionalMask specifies additional configuration changes that the organizer is + * interested in. If the configuration change matches any bit in the mask, + * {@code false} is returned. + */ + static boolean configurationsAreEqualForOrganizer( + Configuration newConfig, @Nullable Configuration oldConfig, + @ActivityInfo.Config int additionalMask) { if (oldConfig == null) { return false; } int cfgChanges = newConfig.diff(oldConfig); + if ((cfgChanges & additionalMask) != 0) { + return false; + } final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration, true /* compareUndefined */) : 0; @@ -2651,6 +2669,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub ownerActivity.getUid(), ownerActivity.info.processName); if (mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder())) { taskFragment.setOverrideOrientation(creationParams.getOverrideOrientation()); + taskFragment.setConfigurationChangeMaskForOrganizer( + creationParams.getConfigurationChangeMask()); } final int position; if (creationParams.getPairedPrimaryFragmentToken() != null) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 1281be5132d3..7030d986494f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -33,6 +34,8 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; +import static android.content.res.Configuration.UI_MODE_NIGHT_NO; +import static android.content.res.Configuration.UI_MODE_NIGHT_YES; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; @@ -1983,6 +1986,30 @@ public class WindowOrganizerTests extends WindowTestsBase { testSetAlwaysOnTop(displayArea); } + @Test + public void testConfigurationsAreEqualForOrganizer() { + Configuration config1 = new Configuration(); + config1.smallestScreenWidthDp = 300; + config1.uiMode = UI_MODE_NIGHT_YES; + + Configuration config2 = new Configuration(config1); + config2.uiMode = UI_MODE_NIGHT_NO; + + Configuration config3 = new Configuration(config1); + config3.smallestScreenWidthDp = 500; + + // Should be equal for non-controllable configuration changes. + assertTrue(WindowOrganizerController.configurationsAreEqualForOrganizer(config1, config2)); + + // Should be unequal for non-controllable configuration changes if the organizer is + // interested in that change. + assertFalse(WindowOrganizerController.configurationsAreEqualForOrganizer( + config1, config2, CONFIG_UI_MODE)); + + // Should be unequal for controllable configuration changes. + assertFalse(WindowOrganizerController.configurationsAreEqualForOrganizer(config1, config3)); + } + private void testSetAlwaysOnTop(WindowContainer wc) { final WindowContainerTransaction t = new WindowContainerTransaction(); t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), true); |