Skip to content

Commit c98b289

Browse files
Massimo CarliAndroid Build Coastguard Worker
authored andcommitted
Improve first opaque activity candidate detection
To catch the right candidate as first opaque activity beneath a translucent one and handle normal permission dialog behaviour and smart links. Fixes: 278661668 Test: Run `atest WmTests:SizeCompatTests` (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bcf680d064ea8921091bfc4620b6958adf79e8a2) Merged-In: I62e829555c43136080ee4909f7dcf8c388165e9f Change-Id: I62e829555c43136080ee4909f7dcf8c388165e9f
1 parent 1ccf8fc commit c98b289

3 files changed

Lines changed: 147 additions & 78 deletions

File tree

services/core/java/com/android/server/wm/ActivityRecord.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1612,7 +1612,7 @@ void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer
16121612
newParent.setResumedActivity(this, "onParentChanged");
16131613
mImeInsetsFrozenUntilStartInput = false;
16141614
}
1615-
mLetterboxUiController.onActivityParentChanged(newParent);
1615+
mLetterboxUiController.updateInheritedLetterbox();
16161616
}
16171617

16181618
if (rootTask != null && rootTask.topRunningActivity() == this) {

services/core/java/com/android/server/wm/LetterboxUiController.java

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@
112112
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
113113

114114
import java.io.PrintWriter;
115+
import java.util.ArrayList;
116+
import java.util.List;
115117
import java.util.Optional;
116118
import java.util.function.BooleanSupplier;
117119
import java.util.function.Consumer;
@@ -126,8 +128,7 @@
126128
final class LetterboxUiController {
127129

128130
private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
129-
activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing()
130-
&& activityRecord.nowVisible;
131+
activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing();
131132

132133
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
133134

@@ -185,6 +186,10 @@ final class LetterboxUiController {
185186
// Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS
186187
private final boolean mIsOverrideEnableCompatFakeFocusEnabled;
187188

189+
// The list of observers for the destroy event of candidate opaque activities
190+
// when dealing with translucent activities.
191+
private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>();
192+
188193
@Nullable
189194
private final Boolean mBooleanPropertyAllowOrientationOverride;
190195
@Nullable
@@ -198,6 +203,10 @@ final class LetterboxUiController {
198203
@Nullable
199204
private WindowContainerListener mLetterboxConfigListener;
200205

206+
@Nullable
207+
@VisibleForTesting
208+
ActivityRecord mFirstOpaqueActivityBeneath;
209+
201210
private boolean mShowWallpaperForLetterboxBackground;
202211

203212
// In case of transparent activities we might need to access the aspectRatio of the
@@ -361,6 +370,10 @@ void destroy() {
361370
mLetterbox.destroy();
362371
mLetterbox = null;
363372
}
373+
for (int i = mDestroyListeners.size() - 1; i >= 0; i--) {
374+
mDestroyListeners.get(i).updateInheritedLetterbox();
375+
}
376+
mDestroyListeners.clear();
364377
if (mLetterboxConfigListener != null) {
365378
mLetterboxConfigListener.onRemoved();
366379
mLetterboxConfigListener = null;
@@ -1577,7 +1590,11 @@ LetterboxDetails getLetterboxDetails() {
15771590
* first opaque activity beneath.
15781591
* @param parent The parent container.
15791592
*/
1580-
void onActivityParentChanged(WindowContainer<?> parent) {
1593+
void updateInheritedLetterbox() {
1594+
final WindowContainer<?> parent = mActivityRecord.getParent();
1595+
if (parent == null) {
1596+
return;
1597+
}
15811598
if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
15821599
return;
15831600
}
@@ -1587,27 +1604,28 @@ void onActivityParentChanged(WindowContainer<?> parent) {
15871604
}
15881605
// In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the
15891606
// opaque activity constraints because we're expecting the activity is already letterboxed.
1590-
if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
1591-
|| mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
1592-
return;
1593-
}
1594-
final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
1607+
mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
15951608
FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
15961609
mActivityRecord /* boundary */, false /* includeBoundary */,
15971610
true /* traverseTopToBottom */);
1598-
if (firstOpaqueActivityBeneath == null || firstOpaqueActivityBeneath.isEmbedded()) {
1611+
if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) {
15991612
// We skip letterboxing if the translucent activity doesn't have any opaque
16001613
// activities beneath or the activity below is embedded which never has letterbox.
1614+
mActivityRecord.recomputeConfiguration();
16011615
return;
16021616
}
1603-
inheritConfiguration(firstOpaqueActivityBeneath);
1617+
if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
1618+
|| mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
1619+
return;
1620+
}
1621+
mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.add(this);
1622+
inheritConfiguration(mFirstOpaqueActivityBeneath);
16041623
mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
1605-
mActivityRecord, firstOpaqueActivityBeneath,
1606-
(opaqueConfig, transparentConfig) -> {
1607-
final Configuration mutatedConfiguration =
1608-
fromOriginalTranslucentConfig(transparentConfig);
1624+
mActivityRecord, mFirstOpaqueActivityBeneath,
1625+
(opaqueConfig, transparentOverrideConfig) -> {
1626+
resetTranslucentOverrideConfig(transparentOverrideConfig);
16091627
final Rect parentBounds = parent.getWindowConfiguration().getBounds();
1610-
final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds();
1628+
final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds();
16111629
final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
16121630
// We cannot use letterboxBounds directly here because the position relies on
16131631
// letterboxing. Using letterboxBounds directly, would produce a double offset.
@@ -1616,9 +1634,9 @@ void onActivityParentChanged(WindowContainer<?> parent) {
16161634
parentBounds.top + letterboxBounds.height());
16171635
// We need to initialize appBounds to avoid NPE. The actual value will
16181636
// be set ahead when resolving the Configuration for the activity.
1619-
mutatedConfiguration.windowConfiguration.setAppBounds(new Rect());
1620-
inheritConfiguration(firstOpaqueActivityBeneath);
1621-
return mutatedConfiguration;
1637+
transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect());
1638+
inheritConfiguration(mFirstOpaqueActivityBeneath);
1639+
return transparentOverrideConfig;
16221640
});
16231641
}
16241642

@@ -1691,26 +1709,19 @@ Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
16911709
if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) {
16921710
return Optional.empty();
16931711
}
1694-
return Optional.ofNullable(mActivityRecord.getTask().getActivity(
1695-
FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
1696-
mActivityRecord /* boundary */, false /* includeBoundary */,
1697-
true /* traverseTopToBottom */));
1712+
return Optional.ofNullable(mFirstOpaqueActivityBeneath);
16981713
}
16991714

1700-
// When overriding translucent activities configuration we need to keep some of the
1701-
// original properties
1702-
private Configuration fromOriginalTranslucentConfig(Configuration translucentConfig) {
1703-
final Configuration configuration = new Configuration(translucentConfig);
1715+
/** Resets the screen size related fields so they can be resolved by requested bounds later. */
1716+
private static void resetTranslucentOverrideConfig(Configuration config) {
17041717
// The values for the following properties will be defined during the configuration
17051718
// resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the
17061719
// properties inherited from the first not finishing opaque activity beneath.
1707-
configuration.orientation = ORIENTATION_UNDEFINED;
1708-
configuration.screenWidthDp = configuration.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
1709-
configuration.screenHeightDp =
1710-
configuration.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
1711-
configuration.smallestScreenWidthDp =
1712-
configuration.compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
1713-
return configuration;
1720+
config.orientation = ORIENTATION_UNDEFINED;
1721+
config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
1722+
config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
1723+
config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp =
1724+
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
17141725
}
17151726

17161727
private void inheritConfiguration(ActivityRecord firstOpaque) {
@@ -1729,6 +1740,10 @@ private void inheritConfiguration(ActivityRecord firstOpaque) {
17291740
}
17301741

17311742
private void clearInheritedConfig() {
1743+
if (mFirstOpaqueActivityBeneath != null) {
1744+
mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.remove(this);
1745+
}
1746+
mFirstOpaqueActivityBeneath = null;
17321747
mLetterboxConfigListener = null;
17331748
mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
17341749
mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;

services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java

Lines changed: 98 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -174,44 +174,6 @@ private void setUpDisplaySizeWithApp(int dw, int dh) {
174174
setUpApp(builder.build());
175175
}
176176

177-
@Test
178-
public void testActivityInHistoryAndNotVisibleIsNotUsedAsOpaqueForTranslucentActivities() {
179-
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
180-
setUpDisplaySizeWithApp(2000, 1000);
181-
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
182-
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
183-
mActivity.nowVisible = false;
184-
// Translucent Activity
185-
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
186-
.setLaunchedFromUid(mActivity.getUid())
187-
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
188-
.build();
189-
doReturn(false).when(translucentActivity).fillsParent();
190-
191-
mTask.addChild(translucentActivity);
192-
193-
assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
194-
}
195-
196-
@Test
197-
public void testActivityInHistoryAndVisibleIsUsedAsOpaqueForTranslucentActivities() {
198-
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
199-
setUpDisplaySizeWithApp(2000, 1000);
200-
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
201-
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
202-
mActivity.nowVisible = true;
203-
// Translucent Activity
204-
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
205-
.setLaunchedFromUid(mActivity.getUid())
206-
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
207-
.build();
208-
doReturn(false).when(translucentActivity).fillsParent();
209-
210-
mTask.addChild(translucentActivity);
211-
212-
assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
213-
}
214-
215177
@Test
216178
public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
217179
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
@@ -236,7 +198,6 @@ public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
236198
public void testHorizontalReachabilityEnabledForTranslucentActivities() {
237199
setUpDisplaySizeWithApp(2500, 1000);
238200
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
239-
mActivity.nowVisible = true;
240201
final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
241202
config.setTranslucentLetterboxingOverrideEnabled(true);
242203
config.setLetterboxHorizontalPositionMultiplier(0.5f);
@@ -312,7 +273,6 @@ public void testHorizontalReachabilityEnabledForTranslucentActivities() {
312273
public void testVerticalReachabilityEnabledForTranslucentActivities() {
313274
setUpDisplaySizeWithApp(1000, 2500);
314275
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
315-
mActivity.nowVisible = true;
316276
final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
317277
config.setTranslucentLetterboxingOverrideEnabled(true);
318278
config.setLetterboxVerticalPositionMultiplier(0.5f);
@@ -384,14 +344,111 @@ public void testVerticalReachabilityEnabledForTranslucentActivities() {
384344
checkIsCentered.run();
385345
}
386346

347+
@Test
348+
public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
349+
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
350+
setUpDisplaySizeWithApp(2000, 1000);
351+
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
352+
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
353+
// Launch another opaque activity
354+
final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
355+
.setLaunchedFromUid(mActivity.getUid())
356+
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
357+
.build();
358+
mTask.addChild(opaqueActivity);
359+
// Transparent activity strategy not applied
360+
assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
361+
362+
// Launch translucent Activity
363+
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
364+
.setLaunchedFromUid(mActivity.getUid())
365+
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
366+
.build();
367+
doReturn(false).when(translucentActivity).fillsParent();
368+
mTask.addChild(translucentActivity);
369+
// Transparent strategy applied
370+
assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
371+
372+
spyOn(translucentActivity.mLetterboxUiController);
373+
clearInvocations(translucentActivity.mLetterboxUiController);
374+
375+
// We destroy the first opaque activity
376+
opaqueActivity.setState(DESTROYED, "testing");
377+
opaqueActivity.removeImmediately();
378+
379+
// Check that updateInheritedLetterbox() is invoked again
380+
verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
381+
}
382+
383+
@Test
384+
public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
385+
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
386+
setUpDisplaySizeWithApp(2000, 1000);
387+
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
388+
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
389+
390+
// Launch translucent Activity
391+
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
392+
.setLaunchedFromUid(mActivity.getUid())
393+
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
394+
.build();
395+
doReturn(false).when(translucentActivity).fillsParent();
396+
mTask.addChild(translucentActivity);
397+
// Transparent strategy applied
398+
assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
399+
assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
400+
401+
spyOn(translucentActivity.mLetterboxUiController);
402+
clearInvocations(translucentActivity.mLetterboxUiController);
403+
404+
// We destroy the first opaque activity
405+
mActivity.setState(DESTROYED, "testing");
406+
mActivity.removeImmediately();
407+
408+
// Check that updateInheritedLetterbox() is invoked again
409+
verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
410+
assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
411+
}
412+
413+
@Test
414+
public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
415+
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
416+
setUpDisplaySizeWithApp(2000, 1000);
417+
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
418+
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
419+
// Launch another opaque activity
420+
final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
421+
.setLaunchedFromUid(mActivity.getUid())
422+
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
423+
.build();
424+
mTask.addChild(opaqueActivity);
425+
// Transparent activity strategy not applied
426+
assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
427+
428+
// Launch translucent Activity
429+
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
430+
.setLaunchedFromUid(mActivity.getUid())
431+
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
432+
.build();
433+
doReturn(false).when(translucentActivity).fillsParent();
434+
mTask.addChild(translucentActivity);
435+
// Transparent strategy applied
436+
assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
437+
438+
spyOn(translucentActivity.mLetterboxUiController);
439+
clearInvocations(translucentActivity.mLetterboxUiController);
440+
441+
// Check that updateInheritedLetterbox() is invoked again
442+
verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox();
443+
}
444+
387445
@Test
388446
public void testApplyStrategyToTranslucentActivities() {
389447
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
390448
setUpDisplaySizeWithApp(2000, 1000);
391449
prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
392450
mActivity.info.setMinAspectRatio(1.2f);
393451
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
394-
mActivity.nowVisible = true;
395452
// Translucent Activity
396453
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
397454
.setLaunchedFromUid(mActivity.getUid())
@@ -448,7 +505,6 @@ public void testApplyStrategyToMultipleTranslucentActivities() {
448505
prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
449506
mActivity.info.setMinAspectRatio(1.2f);
450507
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
451-
mActivity.nowVisible = true;
452508
// Translucent Activity
453509
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
454510
.setLaunchedFromUid(mActivity.getUid())
@@ -542,7 +598,6 @@ public void testTranslucentActivitiesWhenUnfolding() {
542598
true /* ignoreOrientationRequest */);
543599
mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
544600
1.0f /*letterboxVerticalPositionMultiplier*/);
545-
mActivity.nowVisible = true;
546601
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
547602
// We launch a transparent activity
548603
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
@@ -575,7 +630,6 @@ public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayIn
575630
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
576631
setUpDisplaySizeWithApp(2800, 1400);
577632
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
578-
mActivity.nowVisible = true;
579633
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
580634
// Rotate to put activity in size compat mode.
581635
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);

0 commit comments

Comments
 (0)