summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/PointerIcon.java17
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig7
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java30
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java3
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java67
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java55
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java54
14 files changed, 236 insertions, 61 deletions
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 9099f9855eab..7eb6f2e2b331 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -284,7 +284,7 @@ public final class PointerIcon implements Parcelable {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap must not be null");
}
- validateHotSpot(bitmap, hotSpotX, hotSpotY);
+ validateHotSpot(bitmap, hotSpotX, hotSpotY, false /* isScaled */);
PointerIcon icon = new PointerIcon(TYPE_CUSTOM);
icon.mBitmap = bitmap;
@@ -517,7 +517,9 @@ public final class PointerIcon implements Parcelable {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
- validateHotSpot(bitmap, hotSpotX, hotSpotY);
+ // The bitmap and hotspot are loaded from the context, which means it is implicitly scaled
+ // to the current display density, so treat this as a scaled icon when verifying hotspot.
+ validateHotSpot(bitmap, hotSpotX, hotSpotY, true /* isScaled */);
// Set the properties now that we have successfully loaded the icon.
mBitmap = bitmap;
mHotSpotX = hotSpotX;
@@ -531,11 +533,16 @@ public final class PointerIcon implements Parcelable {
+ ", hotspotX=" + mHotSpotX + ", hotspotY=" + mHotSpotY + "}";
}
- private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY) {
- if (hotSpotX < 0 || hotSpotX >= bitmap.getWidth()) {
+ private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY,
+ boolean isScaled) {
+ // Be more lenient when checking the hotspot for scaled icons to account for the restriction
+ // that bitmaps must have an integer size.
+ if (hotSpotX < 0 || (isScaled ? (int) hotSpotX > bitmap.getWidth()
+ : hotSpotX >= bitmap.getWidth())) {
throw new IllegalArgumentException("x hotspot lies outside of the bitmap area");
}
- if (hotSpotY < 0 || hotSpotY >= bitmap.getHeight()) {
+ if (hotSpotY < 0 || (isScaled ? (int) hotSpotY > bitmap.getHeight()
+ : hotSpotY >= bitmap.getHeight())) {
throw new IllegalArgumentException("y hotspot lies outside of the bitmap area");
}
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 0ef4fa392618..9e2a6cd634de 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -1,13 +1,6 @@
package: "com.android.window.flags"
flag {
- name: "disable_thin_letterboxing_reachability"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether reachability is disabled in case of thin letterboxing"
- bug: "334077350"
-}
-
-flag {
name: "disable_thin_letterboxing_policy"
namespace: "large_screen_experiences_app_compat"
description: "Whether reachability is disabled in case of thin letterboxing"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index a59b95a60879..8f36ef000581 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -40,7 +40,6 @@ fun SettingsTitle(title: String, useMediumWeight: Boolean = false) {
Text(
text = title,
modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
- color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleMedium.withWeight(useMediumWeight),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 42d424869b42..d30925935eaf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -936,6 +936,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mHasActiveSubIdOnDds = false;
Log.e(TAG, "Can't get DDS subscriptionInfo");
return;
+ } else if (ddsSubInfo.isOnlyNonTerrestrialNetwork()) {
+ mHasActiveSubIdOnDds = false;
+ Log.d(TAG, "This is NTN, so do not show mobile data");
+ return;
}
mHasActiveSubIdOnDds = isEmbeddedSubscriptionVisible(ddsSubInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 4eca51d47a36..56016e151f00 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -91,12 +91,19 @@ constructor(
} else {
dismissKeyguard()
}
- transitionCoordinator?.startExit()
+
+ var transitionOptions: ActivityOptions? = null
+ if (transitionCoordinator?.decor?.isAttachedToWindow == true) {
+ transitionCoordinator.startExit()
+ transitionOptions = options
+ }
if (user == myUserHandle()) {
- withContext(mainDispatcher) { context.startActivity(intent, options?.toBundle()) }
+ withContext(mainDispatcher) {
+ context.startActivity(intent, transitionOptions?.toBundle())
+ }
} else {
- launchCrossProfileIntent(user, intent, options?.toBundle())
+ launchCrossProfileIntent(user, intent, transitionOptions?.toBundle())
}
if (overrideTransition) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 6a136974b205..9c55bd589443 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -14,7 +14,9 @@ import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAS
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -1101,6 +1103,34 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse();
}
+ @Test
+ public void hasActiveSubIdOnDds_activeDdsAndIsOnlyNonTerrestrialNetwork_returnFalse() {
+ when(SubscriptionManager.getDefaultDataSubscriptionId())
+ .thenReturn(SUB_ID);
+ SubscriptionInfo info = mock(SubscriptionInfo.class);
+ when(info.isEmbedded()).thenReturn(true);
+ when(info.isOnlyNonTerrestrialNetwork()).thenReturn(true);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
+
+ mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ assertFalse(mInternetDialogController.hasActiveSubIdOnDds());
+ }
+
+ @Test
+ public void hasActiveSubIdOnDds_activeDdsAndIsNotOnlyNonTerrestrialNetwork_returnTrue() {
+ when(SubscriptionManager.getDefaultDataSubscriptionId())
+ .thenReturn(SUB_ID);
+ SubscriptionInfo info = mock(SubscriptionInfo.class);
+ when(info.isEmbedded()).thenReturn(true);
+ when(info.isOnlyNonTerrestrialNetwork()).thenReturn(false);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
+
+ mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ assertTrue(mInternetDialogController.hasActiveSubIdOnDds());
+ }
+
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 877b90f5e5f7..92f08afc80c4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1885,7 +1885,6 @@ public class AudioService extends IAudioService.Stub
}
mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
- mSoundDoseHelper.reset();
// Restore rotation information.
if (mMonitorRotation) {
@@ -1896,6 +1895,8 @@ public class AudioService extends IAudioService.Stub
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
+ mSoundDoseHelper.reset(/*resetISoundDose=*/true);
+
sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
SENDMSG_QUEUE, 1, 0, null, 0);
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 9610034caf01..e28ae952e65a 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -856,11 +856,12 @@ public class SoundDoseHelper {
pw.println();
}
- /*package*/void reset() {
+ /*package*/void reset(boolean resetISoundDose) {
Log.d(TAG, "Reset the sound dose helper");
- mSoundDose.compareAndExchange(/*expectedValue=*/null,
- AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+ if (resetISoundDose) {
+ mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+ }
synchronized (mCsdStateLock) {
try {
@@ -972,7 +973,7 @@ public class SoundDoseHelper {
}
}
- reset();
+ reset(/*resetISoundDose=*/false);
}
private void onConfigureSafeMedia(boolean force, String caller) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c814a1ef1c13..7625ad8393b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6267,9 +6267,22 @@ public class PackageManagerService implements PackageSender, TestUtilityService
packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
});
if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
- Binder.withCleanCallingIdentity(() ->
- mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
- UserHandle.USER_ALL));
+ Binder.withCleanCallingIdentity(() -> {
+ mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
+ UserHandle.USER_ALL);
+ // Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes
+ final Computer snapShot = snapshotComputer();
+ final ArrayList<String> components = new ArrayList<>(
+ Collections.singletonList(packageName));
+ final int appId = packageState.getAppId();
+ final int[] userIds = resolveUserIds(UserHandle.USER_ALL);
+ final String reason = "The mimeGroup is changed";
+ for (int i = 0; i < userIds.length; i++) {
+ final int packageUid = UserHandle.getUid(userIds[i], appId);
+ mBroadcastHelper.sendPackageChangedBroadcast(snapShot, packageName,
+ true /* dontKillApp */, components, packageUid, reason);
+ }
+ });
}
scheduleWriteSettings();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 65d5c0490cfc..78cb8d0f2ace 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -76,7 +76,6 @@ import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
@@ -86,6 +85,7 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
@@ -311,6 +311,7 @@ import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -352,6 +353,7 @@ import android.view.RemoteAnimationTarget;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -8533,6 +8535,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (isFixedOrientationLetterboxAllowed) {
resolveFixedOrientationConfiguration(newParentConfiguration);
}
+ // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
+ // are already calculated in resolveFixedOrientationConfiguration.
+ // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+ if (!isLetterboxedForFixedOrientationAndAspectRatio()
+ && !mLetterboxUiController.hasFullscreenOverride()) {
+ resolveAspectRatioRestriction(newParentConfiguration);
+ }
final CompatDisplayInsets compatDisplayInsets = getCompatDisplayInsets();
if (compatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration, compatDisplayInsets);
@@ -8545,14 +8554,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!matchParentBounds()) {
computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
}
- // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
- // are already calculated in resolveFixedOrientationConfiguration, or if in size compat
- // mode, it should already be calculated in resolveSizeCompatModeConfiguration.
- // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- }
- if (!isLetterboxedForFixedOrientationAndAspectRatio() && !mInSizeCompatModeForBounds
- && !mLetterboxUiController.hasFullscreenOverride()) {
- resolveAspectRatioRestriction(newParentConfiguration);
}
if (isFixedOrientationLetterboxAllowed || compatDisplayInsets != null
@@ -8764,7 +8765,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
}
// Letterbox for limited aspect ratio.
- if (mIsAspectRatioApplied) {
+ if (isLetterboxedForAspectRatioOnly()) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
}
@@ -8793,13 +8794,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final float screenResolvedBoundsWidth = screenResolvedBounds.width();
final float parentAppBoundsWidth = parentAppBounds.width();
+ final boolean isImmersiveMode = isImmersiveMode(parentBounds);
+ final Insets navBarInsets;
+ if (isImmersiveMode) {
+ navBarInsets = mDisplayContent.getInsetsStateController()
+ .getRawInsetsState().calculateInsets(
+ parentBounds,
+ WindowInsets.Type.navigationBars(),
+ true /* ignoreVisibility */);
+ } else {
+ navBarInsets = Insets.NONE;
+ }
// Horizontal position
int offsetX = 0;
if (parentBounds.width() != screenResolvedBoundsWidth) {
if (screenResolvedBoundsWidth <= parentAppBoundsWidth) {
float positionMultiplier = mLetterboxUiController.getHorizontalPositionMultiplier(
newParentConfiguration);
- offsetX = Math.max(0, (int) Math.ceil((parentAppBoundsWidth
+ // If in immersive mode, always align to right and overlap right insets (task bar)
+ // as they are transient and hidden. This removes awkward right spacing.
+ final int appWidth = (int) (parentAppBoundsWidth + navBarInsets.right);
+ offsetX = Math.max(0, (int) Math.ceil((appWidth
- screenResolvedBoundsWidth) * positionMultiplier)
// This is added to make sure that insets added inside
// CompatDisplayInsets#getContainerBounds() do not break the alignment
@@ -8819,9 +8834,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
newParentConfiguration);
// If in immersive mode, always align to bottom and overlap bottom insets (nav bar,
// task bar) as they are transient and hidden. This removes awkward bottom spacing.
- final float newHeight = mDisplayContent.getDisplayPolicy().isImmersiveMode()
- ? parentBoundsHeight : parentAppBoundsHeight;
- offsetY = Math.max(0, (int) Math.ceil((newHeight
+ final int appHeight = (int) (parentAppBoundsHeight + navBarInsets.bottom);
+ offsetY = Math.max(0, (int) Math.ceil((appHeight
- screenResolvedBoundsHeight) * positionMultiplier)
// This is added to make sure that insets added inside
// CompatDisplayInsets#getContainerBounds() do not break the alignment
@@ -8841,7 +8855,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If the top is aligned with parentAppBounds add the vertical insets back so that the app
// content aligns with the status bar
- if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top) {
+ if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top
+ && !isImmersiveMode) {
resolvedConfig.windowConfiguration.getBounds().top = parentBounds.top;
if (mSizeCompatBounds != null) {
mSizeCompatBounds.top = parentBounds.top;
@@ -8864,6 +8879,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean isImmersiveMode(@NonNull Rect parentBounds) {
+ if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds) {
+ return false;
+ }
+ final Insets navBarInsets = mDisplayContent.getInsetsStateController()
+ .getRawInsetsState().calculateInsets(
+ parentBounds,
+ WindowInsets.Type.navigationBars(),
+ false /* ignoreVisibility */);
+ return Insets.NONE.equals(navBarInsets);
+ }
+
@NonNull Rect getScreenResolvedBounds() {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
@@ -8906,6 +8933,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mLetterboxBoundsForFixedOrientationAndAspectRatio != null;
}
+ boolean isLetterboxedForAspectRatioOnly() {
+ return mLetterboxBoundsForAspectRatio != null;
+ }
+
boolean isAspectRatioApplied() {
return mIsAspectRatioApplied;
}
@@ -9198,11 +9229,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// orientation bounds (stored in resolved bounds) instead of parent bounds since the
// activity will be displayed within them even if it is in size compat mode. They should be
// saved here before resolved bounds are overridden below.
- final Rect containerBounds = isLetterboxedForFixedOrientationAndAspectRatio()
+ final Rect containerBounds = isAspectRatioApplied()
? new Rect(resolvedBounds)
: newParentConfiguration.windowConfiguration.getBounds();
- final Rect containerAppBounds = isLetterboxedForFixedOrientationAndAspectRatio()
- ? new Rect(getResolvedOverrideConfiguration().windowConfiguration.getAppBounds())
+ final Rect containerAppBounds = isAspectRatioApplied()
+ ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
: newParentConfiguration.windowConfiguration.getAppBounds();
final int requestedOrientation = getRequestedConfigurationOrientation();
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 691176a794ae..7192a20935da 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1686,7 +1686,7 @@ final class LetterboxUiController {
if (mainWin.isLetterboxedForDisplayCutout()) {
return "DISPLAY_CUTOUT";
}
- if (mActivityRecord.isAspectRatioApplied()) {
+ if (mActivityRecord.isLetterboxedForAspectRatioOnly()) {
return "ASPECT_RATIO";
}
return "UNKNOWN_REASON";
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b6760c55c0cd..944b82112482 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5793,7 +5793,7 @@ class Task extends TaskFragment {
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
// current task to be selected.
- if (isTopRootTaskInDisplayArea() && mAtmService.mController != null) {
+ if (mAtmService.mController != null && isTopRootTaskInDisplayArea()) {
ActivityRecord next = topRunningActivity(null, task.mTaskId);
if (next == null) {
next = topRunningActivity(null, INVALID_TASK_ID);
@@ -5837,6 +5837,15 @@ class Task extends TaskFragment {
+ tr.mTaskId);
if (mTransitionController.isShellTransitionsEnabled()) {
+ // TODO(b/277838915): Consider to make it concurrent to eliminate the special case.
+ final Transition collecting = mTransitionController.getCollectingTransition();
+ if (collecting != null && collecting.mType == TRANSIT_OPEN) {
+ // It can be a CLOSING participate of an OPEN transition. This avoids the deferred
+ // transition from moving task to back after the task was moved to front.
+ collecting.collect(tr);
+ moveTaskToBackInner(tr, collecting);
+ return true;
+ }
final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
mTransitionController, mWmService.mSyncEngine);
// Guarantee that this gets its own transition by queueing on SyncEngine
@@ -5865,7 +5874,7 @@ class Task extends TaskFragment {
return true;
}
- private boolean moveTaskToBackInner(@NonNull Task task, @Nullable Transition transition) {
+ private void moveTaskToBackInner(@NonNull Task task, @Nullable Transition transition) {
final Transition.ReadyCondition movedToBack =
new Transition.ReadyCondition("moved-to-back", task);
if (transition != null) {
@@ -5880,7 +5889,7 @@ class Task extends TaskFragment {
if (inPinnedWindowingMode()) {
mTaskSupervisor.removeRootTask(this);
- return true;
+ return;
}
mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
@@ -5903,7 +5912,6 @@ class Task extends TaskFragment {
} else {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
- return true;
}
boolean willActivityBeVisible(IBinder token) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index bf15bc89be97..c6476df1e3c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -108,6 +108,7 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.view.InsetsFrameProvider;
@@ -123,6 +124,7 @@ import com.android.internal.policy.SystemBarUtils;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.DeviceStateController.DeviceState;
+import com.android.window.flags.Flags;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -187,6 +189,7 @@ public class SizeCompatTests extends WindowTestsBase {
private void setUpApp(DisplayContent display) {
mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
mActivity = mTask.getTopNonFinishingActivity();
+ doReturn(false).when(mActivity).isImmersiveMode(any());
}
private void setUpDisplaySizeWithApp(int dw, int dh) {
@@ -395,6 +398,55 @@ public class SizeCompatTests extends WindowTestsBase {
verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
}
+ // TODO(b/333663877): Enable test after fix
+ @Test
+ @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION})
+ public void testRepositionLandscapeImmersiveAppWithDisplayCutout() {
+ final int dw = 2100;
+ final int dh = 2000;
+ final int cutoutHeight = 150;
+ final TestDisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh)
+ .setCanRotate(false)
+ .setNotch(cutoutHeight)
+ .build();
+ setUpApp(display);
+ display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+ mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+ doReturn(true).when(mActivity).isImmersiveMode(any());
+ prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ SCREEN_ORIENTATION_LANDSCAPE);
+ addWindowToActivity(mActivity);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ final Function<ActivityRecord, Rect> innerBoundsOf =
+ (ActivityRecord a) -> {
+ final Rect bounds = new Rect();
+ a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+ return bounds;
+ };
+
+ final Consumer<Integer> doubleClick =
+ (Integer y) -> {
+ mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ final Rect bounds = mActivity.getBounds();
+ assertTrue(bounds.top > cutoutHeight && bounds.bottom < dh);
+ assertEquals(dw, bounds.width());
+
+ // Double click bottom.
+ doubleClick.accept(dh - 10);
+ assertEquals(dh, innerBoundsOf.apply(mActivity).bottom);
+
+ // Double click top.
+ doubleClick.accept(10);
+ doubleClick.accept(10);
+ assertEquals(cutoutHeight, innerBoundsOf.apply(mActivity).top);
+ }
+
@Test
public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
@@ -3994,8 +4046,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Prepare unresizable landscape activity
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
- final DisplayPolicy displayPolicy = mActivity.mDisplayContent.getDisplayPolicy();
- doReturn(immersive).when(displayPolicy).isImmersiveMode();
+ doReturn(immersive).when(mActivity).isImmersiveMode(any());
mActivity.mRootWindowContainer.performSurfacePlacement();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index f76491afe008..4994d6a2bb3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -64,8 +64,10 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -2373,9 +2375,7 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(transitA.isCollecting());
// finish collecting A
- transitA.start();
- transitA.setAllReady();
- mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ tryFinishTransitionSyncSet(transitA);
waitUntilHandlersIdle();
assertTrue(transitA.isPlaying());
@@ -2481,6 +2481,36 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testDeferredMoveTaskToBack() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity.getTask();
+ registerTestTransitionPlayer();
+ final TransitionController controller = mWm.mRoot.mTransitionController;
+ mSyncEngine = createTestBLASTSyncEngine();
+ controller.setSyncEngine(mSyncEngine);
+ final Transition transition = createTestTransition(TRANSIT_CHANGE, controller);
+ controller.moveToCollecting(transition);
+ task.moveTaskToBack(task);
+ // Actual action will be deferred by current transition.
+ verify(task, never()).moveToBack(any(), any());
+
+ tryFinishTransitionSyncSet(transition);
+ waitUntilHandlersIdle();
+ // Continue to move task to back after the transition is done.
+ verify(task).moveToBack(any(), any());
+ final Transition moveBackTransition = controller.getCollectingTransition();
+ assertNotNull(moveBackTransition);
+ moveBackTransition.abort();
+
+ // The move-to-back can be collected in to a collecting OPEN transition.
+ clearInvocations(task);
+ final Transition transition2 = createTestTransition(TRANSIT_OPEN, controller);
+ controller.moveToCollecting(transition2);
+ task.moveTaskToBack(task);
+ verify(task).moveToBack(any(), any());
+ }
+
+ @Test
public void testNoSyncFlagIfOneTrack() {
final TransitionController controller = mAtm.getTransitionController();
final TestTransitionPlayer player = registerTestTransitionPlayer();
@@ -2497,17 +2527,11 @@ public class TransitionTests extends WindowTestsBase {
controller.startCollectOrQueue(transitC, (deferred) -> {});
// Verify that, as-long as there is <= 1 track, we won't get a SYNC flag
- transitA.start();
- transitA.setAllReady();
- mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ tryFinishTransitionSyncSet(transitA);
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
- transitB.start();
- transitB.setAllReady();
- mSyncEngine.tryFinishForTest(transitB.getSyncId());
+ tryFinishTransitionSyncSet(transitB);
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
- transitC.start();
- transitC.setAllReady();
- mSyncEngine.tryFinishForTest(transitC.getSyncId());
+ tryFinishTransitionSyncSet(transitC);
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
}
@@ -2616,6 +2640,12 @@ public class TransitionTests extends WindowTestsBase {
assertEquals("reason1", condition1.mAlternate);
}
+ private void tryFinishTransitionSyncSet(Transition transition) {
+ transition.setAllReady();
+ transition.start();
+ mSyncEngine.tryFinishForTest(transition.getSyncId());
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {