diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-09-01 14:21:12 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-09-01 14:21:12 +0000 |
commit | 0ddbc3bcc639991c084ef5fb3a245da747c00566 (patch) | |
tree | aab5fb3f976976a5b6db6d3ef1589ab335028e29 | |
parent | 292f896457b95d1d129a2d26d63335339d6dc326 (diff) | |
parent | 79589458225b62433214f51445c16ff50daa911b (diff) | |
download | base-android13-qpr3-s10-release.tar.gz |
Merge cherrypicks of ['googleplex-android-review.googlesource.com/22791220', 'googleplex-android-review.googlesource.com/22569796', 'googleplex-android-review.googlesource.com/22670803', 'googleplex-android-review.googlesource.com/22901260', 'googleplex-android-review.googlesource.com/23053887', 'googleplex-android-review.googlesource.com/23054378', 'googleplex-android-review.googlesource.com/23103421', 'googleplex-android-review.googlesource.com/23048037', 'googleplex-android-review.googlesource.com/22733606', 'googleplex-android-review.googlesource.com/23428200', 'googleplex-android-review.googlesource.com/23479325', 'googleplex-android-review.googlesource.com/22912038', 'googleplex-android-review.googlesource.com/22915981', 'googleplex-android-review.googlesource.com/23511619'] into sparse-10750268-L94100000962880297.android-13.0.0_r76android13-qpr3-s10-release
SPARSE_CHANGE: I7bc69ddf1c1742bd800eea4068ce268dfd96d0e3
SPARSE_CHANGE: Ib3240c0b5037a3a20692c83e2daa8cd858125ca7
SPARSE_CHANGE: Ibd6d9a8377e4b5327a473f8b9adf4e569e826172
SPARSE_CHANGE: Ie12843d00c5336e5a472624205e4e4b9ec881cc5
SPARSE_CHANGE: I98618477a828eb72b2173af6988e804471139e81
SPARSE_CHANGE: Ic7b1c4b40960fd04de9efbf4f6d7abee45c93025
SPARSE_CHANGE: Ib4eef40a0f59512c669b069532e55d36293f9e1c
SPARSE_CHANGE: I62e829555c43136080ee4909f7dcf8c388165e9f
SPARSE_CHANGE: I1c51c6f66cd6967651068de1ffc2e6e8566f5a46
SPARSE_CHANGE: I02ddaa5e823379510e9c81a8d803d5269e6d85ad
SPARSE_CHANGE: If1c14cc0a4e3fbdfbed2c105d37ece9a866f18ed
SPARSE_CHANGE: I35ba4652a125c8c83e18138f0fb0a51f3ef65b73
SPARSE_CHANGE: I2bfdc7801cec1b3aaa44f841d8a821214c6cb801
SPARSE_CHANGE: Ide3143558c906ab650e6a295e805adae81eff9bb
Change-Id: If8b71b4135a215b0a1edf81b64b300980161ba49
25 files changed, 330 insertions, 159 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 514083cefca9..9891409a08bb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -572,6 +572,9 @@ docked if the dock is configured to enable the accelerometer. --> <bool name="config_supportAutoRotation">true</bool> + <!-- If true, allows rotation resolver service to help resolve screen rotation. --> + <bool name="config_allowRotationResolver">true</bool> + <!-- If true, the screen can be rotated via the accelerometer in all 4 rotations as the default behavior. --> <bool name="config_allowAllRotations">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 17a8a76bd590..01c6b5509cb7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1754,6 +1754,7 @@ <java-symbol type="bool" name="config_perDisplayFocusEnabled" /> <java-symbol type="bool" name="config_showNavigationBar" /> <java-symbol type="bool" name="config_supportAutoRotation" /> + <java-symbol type="bool" name="config_allowRotationResolver" /> <java-symbol type="bool" name="config_dockedStackDividerFreeSnapMode" /> <java-symbol type="dimen" name="docked_stack_divider_thickness" /> <java-symbol type="dimen" name="docked_stack_divider_insets" /> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index b4acd6046182..ffc56b6f6106 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -727,10 +727,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange getRefBounds2(mTempRect); t.setPosition(leash2, mTempRect.left, mTempRect.top) .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); - // Make right or bottom side surface always higher than left or top side to avoid weird - // animation when dismiss split. e.g. App surface fling above on decor surface. - t.setLayer(leash1, 1); - t.setLayer(leash2, 2); if (mImePositionProcessor.adjustSurfaceLayoutForIme( t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 77939c7c6964..fbeff255b45d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -468,26 +468,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback) { - boolean openingToSide = false; - if (apps != null) { - for (int i = 0; i < apps.length; ++i) { - if (apps[i].mode == MODE_OPENING - && mSideStage.containsTask(apps[i].taskId)) { - openingToSide = true; - break; - } - } - } else if (mSideStage.getChildCount() != 0) { - // There are chances the entering app transition got canceled by performing - // rotation transition. Checks if there is any child task existed in split - // screen before fallback to cancel entering flow. - openingToSide = true; - } - - if (isEnteringSplit && !openingToSide) { + if (isEnteringSplit && mSideStage.getChildCount() == 0) { mMainExecutor.execute(() -> exitSplitScreen( - mSideStage.getChildCount() == 0 ? mMainStage : mSideStage, - EXIT_REASON_UNKNOWN)); + null /* childrenToTop */, EXIT_REASON_UNKNOWN)); + mSplitUnsupportedToast.show(); } if (finishedCallback != null) { @@ -572,26 +556,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t) { - boolean openingToSide = false; - if (apps != null) { - for (int i = 0; i < apps.length; ++i) { - if (apps[i].mode == MODE_OPENING - && mSideStage.containsTask(apps[i].taskId)) { - openingToSide = true; - break; - } - } - } else if (mSideStage.getChildCount() != 0) { - // There are chances the entering app transition got canceled by performing - // rotation transition. Checks if there is any child task existed in split - // screen before fallback to cancel entering flow. - openingToSide = true; - } - - if (isEnteringSplit && !openingToSide && apps != null) { + if (isEnteringSplit && mSideStage.getChildCount() == 0) { mMainExecutor.execute(() -> exitSplitScreen( - mSideStage.getChildCount() == 0 ? mMainStage : mSideStage, - EXIT_REASON_UNKNOWN)); + null /* childrenToTop */, EXIT_REASON_UNKNOWN)); + mSplitUnsupportedToast.show(); } if (apps != null) { @@ -1034,7 +1002,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { mIsDividerRemoteAnimating = false; mShouldUpdateRecents = true; - mSplitRequest = null; + clearRequestIfPresented(); // If any stage has no child after animation finished, it means that split will display // nothing, such status will happen if task and intent is same app but not support // multi-instance, we should exit split and expand that app as full screen. @@ -1054,7 +1022,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) { mIsDividerRemoteAnimating = false; mShouldUpdateRecents = true; - mSplitRequest = null; + clearRequestIfPresented(); // If any stage has no child after finished animation, that side of the split will display // nothing. This might happen if starting the same app on the both sides while not // supporting multi-instance. Exit the split screen and expand that app to full screen. @@ -1320,6 +1288,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); mShouldUpdateRecents = false; mIsDividerRemoteAnimating = false; + mSplitRequest = null; mSplitLayout.getInvisibleBounds(mTempRect1); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { @@ -1412,6 +1381,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + private void clearRequestIfPresented() { + if (mSideStageListener.mVisible && mSideStageListener.mHasChildren + && mMainStageListener.mVisible && mSideStageListener.mHasChildren) { + mSplitRequest = null; + } + } + /** * Returns whether the split pair in the recent tasks list should be broken. */ @@ -1776,6 +1752,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, true /* setReparentLeafTaskIfRelaunch */); setRootForceTranslucent(true, wct); } else { + clearRequestIfPresented(); wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false /* setReparentLeafTaskIfRelaunch */); setRootForceTranslucent(false, wct); @@ -1926,7 +1903,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { mShouldUpdateRecents = true; - mSplitRequest = null; + clearRequestIfPresented(); updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { @@ -2565,6 +2542,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }); mShouldUpdateRecents = false; + mSplitRequest = null; // Update local states. setSplitsVisible(false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index ead0bcd15c73..a841b7f96d3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -220,20 +220,12 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mCallbacks.onNoLongerSupportMultiWindow(); return; } - if (taskInfo.topActivity == null && mChildrenTaskInfo.contains(taskInfo.taskId) - && mChildrenTaskInfo.get(taskInfo.taskId).topActivity != null) { - // If top activity become null, it means the task is about to vanish, we use this - // signal to remove it from children list earlier for smooth dismiss transition. - mChildrenTaskInfo.remove(taskInfo.taskId); - mChildrenLeashes.remove(taskInfo.taskId); - } else { - mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - } + mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, taskInfo.isVisible); - if (!ENABLE_SHELL_TRANSITIONS && mChildrenLeashes.contains(taskInfo.taskId)) { - updateChildTaskSurface(taskInfo, mChildrenLeashes.get(taskInfo.taskId), - false /* firstAppeared */); + if (!ENABLE_SHELL_TRANSITIONS) { + updateChildTaskSurface( + taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); } } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo @@ -267,6 +259,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } sendStatusChanged(); + } else { + throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + + "\n mRootTaskInfo: " + mRootTaskInfo); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index b31e20fcc438..5ee8bf3006a3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -126,6 +126,12 @@ public final class StageTaskListenerTests extends ShellTestCase { verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true)); } + @Test(expected = IllegalArgumentException.class) + public void testUnknownTaskVanished() { + final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); + mStageTaskListener.onTaskVanished(task); + } + @Test public void testTaskVanished() { // With shell transitions, the transition manages status changes, so skip this test. diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index 666f32939206..cbee104aa3b0 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -368,6 +368,14 @@ void VulkanSurface::releaseBuffers() { } } +void VulkanSurface::invalidateBuffers() { + for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) { + VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i]; + bufferInfo.hasValidContents = false; + bufferInfo.lastPresentedCount = 0; + } +} + VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct // value at the end of the function if everything dequeued correctly. @@ -400,6 +408,10 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer. mWindowInfo.actualSize = actualSize; releaseBuffers(); + } else { + // A change in transform means we need to repaint the entire buffer area as the damage + // rects have just moved about. + invalidateBuffers(); } if (transformHint != mWindowInfo.transform) { diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index b8ccf7810b5d..ff328b25d195 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -113,6 +113,7 @@ private: WindowInfo* outWindowInfo); static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo); void releaseBuffers(); + void invalidateBuffers(); // TODO: This number comes from ui/BufferQueueDefs. We're not pulling the // header in so that we don't need to depend on libui, but we should share diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index 3808e73ca085..5a3d21eacc14 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -214,7 +214,6 @@ open class ControlsProviderSelectorActivity @Inject constructor( putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true) } startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) - animateExitAndFinish() } } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt index 78e132ff6397..4b297a3d321d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt @@ -33,10 +33,10 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM import com.android.systemui.smartspace.SmartspacePrecondition import com.android.systemui.smartspace.SmartspaceTargetFilter -import com.android.systemui.smartspace.dagger.SmartspaceModule import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceViewComponent import com.android.systemui.util.concurrency.Execution import java.util.Optional @@ -58,7 +58,7 @@ class DreamSmartspaceController @Inject constructor( @Named(DREAM_SMARTSPACE_TARGET_FILTER) private val optionalTargetFilter: Optional<SmartspaceTargetFilter>, @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>, - @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN) + @Named(DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN) optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>, ) { companion object { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 7388c27ed313..6cd0aee62551 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -2843,6 +2843,7 @@ public final class NotificationPanelViewController implements Dumpable { /** Set whether the bouncer is showing. */ public void setBouncerShowing(boolean bouncerShowing) { mBouncerShowing = bouncerShowing; + mNotificationStackScrollLayoutController.updateShowEmptyShadeView(); updateVisibility(); } diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt index 641131e4dcc1..03be88fc31d9 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -34,6 +34,11 @@ abstract class SmartspaceModule { const val DREAM_SMARTSPACE_DATA_PLUGIN = "dreams_smartspace_data_plugin" /** + * The BcSmartspaceDataPlugin for the standalone weather on dream. + */ + const val DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN = "dream_weather_smartspace_data_plugin" + + /** * The dream smartspace target filter. */ const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter" @@ -62,6 +67,10 @@ abstract class SmartspaceModule { @Named(DREAM_SMARTSPACE_DATA_PLUGIN) abstract fun optionalDreamsBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin? + @BindsOptionalOf + @Named(DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN) + abstract fun optionalDreamWeatherSmartspaceDataPlugin(): BcSmartspaceDataPlugin? + @Binds @Named(DREAM_SMARTSPACE_PRECONDITION) abstract fun bindSmartspacePrecondition( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 906b9592e3dd..f6c260a875ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1240,7 +1240,8 @@ public class NotificationStackScrollLayoutController { // Hide empty shade view when in transition to Keyguard. // That avoids "No Notifications" to blink when transitioning to AOD. // For more details, see: b/228790482 - && !isInTransitionToKeyguard(); + && !isInTransitionToKeyguard() + && !mCentralSurfaces.isBouncerShowing(); mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt index 8dfd22378a14..82aaccdbbf0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt @@ -144,7 +144,7 @@ class ControlsProviderSelectorActivityTest : SysuiTestCase() { assertThat(activityRule.activity.lastStartedActivity?.component?.className) .isEqualTo(ControlsFavoritingActivity::class.java.name) - assertThat(activityRule.activity.triedToFinish).isTrue() + assertThat(activityRule.activity.triedToFinish).isFalse() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 45ae96c10345..ac60626ba768 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -295,6 +295,34 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { } @Test + public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + + when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); + setupShowEmptyShadeViewState(true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + /* visible= */ false, + /* areNotificationsHiddenInShade= */ false); + } + + @Test + public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + + when(mCentralSurfaces.isBouncerShowing()).thenReturn(false); + setupShowEmptyShadeViewState(true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + /* visible= */ true, + /* areNotificationsHiddenInShade= */ false); + } + + @Test public void testOnUserChange_verifySensitiveProfile() { when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d872ada1fd0c..a6236251635a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1612,7 +1612,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A newParent.setResumedActivity(this, "onParentChanged"); mImeInsetsFrozenUntilStartInput = false; } - mLetterboxUiController.onActivityParentChanged(newParent); + mLetterboxUiController.updateInheritedLetterbox(); } if (rootTask != null && rootTask.topRunningActivity() == this) { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 988e98f28d10..0da178b2bcd0 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.view.Display.TYPE_INTERNAL; import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; @@ -2382,16 +2381,16 @@ public class DisplayPolicy { private int updateSystemBarsLw(WindowState win, int disableFlags) { final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final boolean multiWindowTaskVisible = + final boolean adjacentTasksVisible = defaultTaskDisplayArea.getRootTask(task -> task.isVisible() - && task.getTopLeafTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) + && task.getAdjacentTask() != null) != null; final boolean freeformRootTaskVisible = defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM); // We need to force showing system bars when the multi-window or freeform root task is // visible. - mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible; + mForceShowSystemBars = adjacentTasksVisible || freeformRootTaskVisible; // We need to force the consumption of the system bars if they are force shown or if they // are controlled by a remote insets controller. mForceConsumeSystemBars = mForceShowSystemBars @@ -2412,7 +2411,7 @@ public class DisplayPolicy { int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS; appearance = configureStatusBarOpacity(appearance); - appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible, + appearance = configureNavBarOpacity(appearance, adjacentTasksVisible, freeformRootTaskVisible); final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 0a1e29ace045..2cff3545cc1c 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -123,6 +123,7 @@ public class DisplayRotation { public final boolean isDefaultDisplay; private final boolean mSupportAutoRotation; + private final boolean mAllowRotationResolver; private final int mLidOpenRotation; private final int mCarDockRotation; private final int mDeskDockRotation; @@ -265,6 +266,8 @@ public class DisplayRotation { mSupportAutoRotation = mContext.getResources().getBoolean(R.bool.config_supportAutoRotation); + mAllowRotationResolver = + mContext.getResources().getBoolean(R.bool.config_allowRotationResolver); mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation); mCarDockRotation = readRotation(R.integer.config_carDockRotation); mDeskDockRotation = readRotation(R.integer.config_deskDockRotation); @@ -1992,7 +1995,8 @@ public class DisplayRotation { @Override public boolean isRotationResolverEnabled() { - return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE + return mAllowRotationResolver + && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE && mCameraRotationMode == CAMERA_ROTATION_ENABLED && !mService.mPowerManager.isPowerSaveMode(); } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 684e787ec4cf..d69542c42548 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -112,6 +112,8 @@ import com.android.internal.statusbar.LetterboxDetails; import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -126,8 +128,7 @@ import java.util.function.Predicate; final class LetterboxUiController { private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE = - activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing() - && activityRecord.nowVisible; + activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing(); private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM; @@ -185,6 +186,10 @@ final class LetterboxUiController { // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS private final boolean mIsOverrideEnableCompatFakeFocusEnabled; + // The list of observers for the destroy event of candidate opaque activities + // when dealing with translucent activities. + private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>(); + @Nullable private final Boolean mBooleanPropertyAllowOrientationOverride; @Nullable @@ -198,6 +203,10 @@ final class LetterboxUiController { @Nullable private WindowContainerListener mLetterboxConfigListener; + @Nullable + @VisibleForTesting + ActivityRecord mFirstOpaqueActivityBeneath; + private boolean mShowWallpaperForLetterboxBackground; // In case of transparent activities we might need to access the aspectRatio of the @@ -361,6 +370,10 @@ final class LetterboxUiController { mLetterbox.destroy(); mLetterbox = null; } + for (int i = mDestroyListeners.size() - 1; i >= 0; i--) { + mDestroyListeners.get(i).updateInheritedLetterbox(); + } + mDestroyListeners.clear(); if (mLetterboxConfigListener != null) { mLetterboxConfigListener.onRemoved(); mLetterboxConfigListener = null; @@ -1577,7 +1590,11 @@ final class LetterboxUiController { * first opaque activity beneath. * @param parent The parent container. */ - void onActivityParentChanged(WindowContainer<?> parent) { + void updateInheritedLetterbox() { + final WindowContainer<?> parent = mActivityRecord.getParent(); + if (parent == null) { + return; + } if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) { return; } @@ -1587,27 +1604,28 @@ final class LetterboxUiController { } // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the // opaque activity constraints because we're expecting the activity is already letterboxed. - if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() - || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { - return; - } - final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity( + mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity( FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */, mActivityRecord /* boundary */, false /* includeBoundary */, true /* traverseTopToBottom */); - if (firstOpaqueActivityBeneath == null) { + if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) { // We skip letterboxing if the translucent activity doesn't have any opaque - // activities beneath + // activities beneath or the activity below is embedded which never has letterbox. + mActivityRecord.recomputeConfiguration(); return; } - inheritConfiguration(firstOpaqueActivityBeneath); + if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() + || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { + return; + } + mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.add(this); + inheritConfiguration(mFirstOpaqueActivityBeneath); mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation( - mActivityRecord, firstOpaqueActivityBeneath, - (opaqueConfig, transparentConfig) -> { - final Configuration mutatedConfiguration = - fromOriginalTranslucentConfig(transparentConfig); + mActivityRecord, mFirstOpaqueActivityBeneath, + (opaqueConfig, transparentOverrideConfig) -> { + resetTranslucentOverrideConfig(transparentOverrideConfig); final Rect parentBounds = parent.getWindowConfiguration().getBounds(); - final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds(); + final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds(); final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds(); // We cannot use letterboxBounds directly here because the position relies on // letterboxing. Using letterboxBounds directly, would produce a double offset. @@ -1616,9 +1634,9 @@ final class LetterboxUiController { parentBounds.top + letterboxBounds.height()); // We need to initialize appBounds to avoid NPE. The actual value will // be set ahead when resolving the Configuration for the activity. - mutatedConfiguration.windowConfiguration.setAppBounds(new Rect()); - inheritConfiguration(firstOpaqueActivityBeneath); - return mutatedConfiguration; + transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect()); + inheritConfiguration(mFirstOpaqueActivityBeneath); + return transparentOverrideConfig; }); } @@ -1691,26 +1709,19 @@ final class LetterboxUiController { if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) { return Optional.empty(); } - return Optional.ofNullable(mActivityRecord.getTask().getActivity( - FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */, - mActivityRecord /* boundary */, false /* includeBoundary */, - true /* traverseTopToBottom */)); + return Optional.ofNullable(mFirstOpaqueActivityBeneath); } - // When overriding translucent activities configuration we need to keep some of the - // original properties - private Configuration fromOriginalTranslucentConfig(Configuration translucentConfig) { - final Configuration configuration = new Configuration(translucentConfig); + /** Resets the screen size related fields so they can be resolved by requested bounds later. */ + private static void resetTranslucentOverrideConfig(Configuration config) { // The values for the following properties will be defined during the configuration // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the // properties inherited from the first not finishing opaque activity beneath. - configuration.orientation = ORIENTATION_UNDEFINED; - configuration.screenWidthDp = configuration.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; - configuration.screenHeightDp = - configuration.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; - configuration.smallestScreenWidthDp = - configuration.compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; - return configuration; + config.orientation = ORIENTATION_UNDEFINED; + config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; + config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; + config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp = + SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; } private void inheritConfiguration(ActivityRecord firstOpaque) { @@ -1729,6 +1740,10 @@ final class LetterboxUiController { } private void clearInheritedConfig() { + if (mFirstOpaqueActivityBeneath != null) { + mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.remove(this); + } + mFirstOpaqueActivityBeneath = null; mLetterboxConfigListener = null; mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f5c44d94160b..1ddc3e8c19b3 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2385,6 +2385,22 @@ class Task extends TaskFragment { return parentTask == null ? null : parentTask.getCreatedByOrganizerTask(); } + /** @return the first adjacent task of this task or its parent. */ + @Nullable + Task getAdjacentTask() { + final TaskFragment adjacentTaskFragment = getAdjacentTaskFragment(); + if (adjacentTaskFragment != null && adjacentTaskFragment.asTask() != null) { + return adjacentTaskFragment.asTask(); + } + + final WindowContainer parent = getParent(); + if (parent == null || parent.asTask() == null) { + return null; + } + + return parent.asTask().getAdjacentTask(); + } + // TODO(task-merge): Figure out what's the right thing to do for places that used it. boolean isRootTask() { return getRootTask() == this; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 02d3af6da326..6ec08dc08c51 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1396,6 +1396,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } void onAppTransitionDone() { + if (mSurfaceFreezer.hasLeash()) { + mSurfaceFreezer.unfreeze(getSyncTransaction()); + } for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer wc = mChildren.get(i); wc.onAppTransitionDone(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 66213cc6403d..0169387c483d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3875,7 +3875,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // configuration update when the window has requested to be hidden. Doing so can lead to // the client erroneously accepting a configuration that would have otherwise caused an // activity restart. We instead hand back the last reported {@link MergedConfiguration}. + // Also note since starting window isn't a window of activity, it won't make activity + // restart, so here should allow starting window to set the last reported configuration + // during relayout, which could happen before activity request visible. if (useLatestConfig || (relayoutVisible && (mActivityRecord == null + || mAttrs.type == TYPE_APPLICATION_STARTING || mActivityRecord.isVisibleRequested()))) { final Configuration globalConfig = getProcessGlobalConfiguration(); final Configuration overrideConfig = getMergedOverrideConfiguration(); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index d3282b97a6b8..9f376ad6edab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -81,12 +81,14 @@ public class InsetsPolicyTest extends WindowTestsBase { } @Test - public void testControlsForDispatch_multiWindowTaskVisible() { + public void testControlsForDispatch_adjacentTasksVisible() { addWindow(TYPE_STATUS_BAR, "statusBar"); addWindow(TYPE_NAVIGATION_BAR, "navBar"); - final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, - ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app"); + final Task task1 = createTask(mDisplayContent); + final Task task2 = createTask(mDisplayContent); + task1.setAdjacentTaskFragment(task2); + final WindowState win = createAppWindow(task1, WINDOWING_MODE_MULTI_WINDOW, "app"); final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win); // The app must not control any system bars. 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 d93871e59b37..637ec7e92a7d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -175,44 +175,6 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testActivityInHistoryAndNotVisibleIsNotUsedAsOpaqueForTranslucentActivities() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = false; - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - doReturn(false).when(translucentActivity).fillsParent(); - - mTask.addChild(translucentActivity); - - assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - } - - @Test - public void testActivityInHistoryAndVisibleIsUsedAsOpaqueForTranslucentActivities() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = true; - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - doReturn(false).when(translucentActivity).fillsParent(); - - mTask.addChild(translucentActivity); - - assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - } - - @Test public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() { mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); setUpDisplaySizeWithApp(2000, 1000); @@ -236,7 +198,6 @@ public class SizeCompatTests extends WindowTestsBase { public void testHorizontalReachabilityEnabledForTranslucentActivities() { setUpDisplaySizeWithApp(2500, 1000); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = true; final LetterboxConfiguration config = mWm.mLetterboxConfiguration; config.setTranslucentLetterboxingOverrideEnabled(true); config.setLetterboxHorizontalPositionMultiplier(0.5f); @@ -312,7 +273,6 @@ public class SizeCompatTests extends WindowTestsBase { public void testVerticalReachabilityEnabledForTranslucentActivities() { setUpDisplaySizeWithApp(1000, 2500); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = true; final LetterboxConfiguration config = mWm.mLetterboxConfiguration; config.setTranslucentLetterboxingOverrideEnabled(true); config.setLetterboxVerticalPositionMultiplier(0.5f); @@ -385,13 +345,110 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testApplyStrategyAgainWhenOpaqueIsDestroyed() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2000, 1000); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + // Launch another opaque activity + final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + mTask.addChild(opaqueActivity); + // Transparent activity strategy not applied + assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + + // Launch translucent Activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + doReturn(false).when(translucentActivity).fillsParent(); + mTask.addChild(translucentActivity); + // Transparent strategy applied + assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + + spyOn(translucentActivity.mLetterboxUiController); + clearInvocations(translucentActivity.mLetterboxUiController); + + // We destroy the first opaque activity + opaqueActivity.setState(DESTROYED, "testing"); + opaqueActivity.removeImmediately(); + + // Check that updateInheritedLetterbox() is invoked again + verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox(); + } + + @Test + public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2000, 1000); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Launch translucent Activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + doReturn(false).when(translucentActivity).fillsParent(); + mTask.addChild(translucentActivity); + // Transparent strategy applied + assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath); + + spyOn(translucentActivity.mLetterboxUiController); + clearInvocations(translucentActivity.mLetterboxUiController); + + // We destroy the first opaque activity + mActivity.setState(DESTROYED, "testing"); + mActivity.removeImmediately(); + + // Check that updateInheritedLetterbox() is invoked again + verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox(); + assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath); + } + + @Test + public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2000, 1000); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + // Launch another opaque activity + final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + mTask.addChild(opaqueActivity); + // Transparent activity strategy not applied + assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + + // Launch translucent Activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + doReturn(false).when(translucentActivity).fillsParent(); + mTask.addChild(translucentActivity); + // Transparent strategy applied + assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + + spyOn(translucentActivity.mLetterboxUiController); + clearInvocations(translucentActivity.mLetterboxUiController); + + // Check that updateInheritedLetterbox() is invoked again + verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox(); + } + + @Test public void testApplyStrategyToTranslucentActivities() { mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); setUpDisplaySizeWithApp(2000, 1000); prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); mActivity.info.setMinAspectRatio(1.2f); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = true; // Translucent Activity final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) .setLaunchedFromUid(mActivity.getUid()) @@ -448,7 +505,6 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); mActivity.info.setMinAspectRatio(1.2f); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = true; // Translucent Activity final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) .setLaunchedFromUid(mActivity.getUid()) @@ -475,7 +531,25 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testTranslucentActivitiesDontGoInSizeCompatMode() { + public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2000, 1000); + mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + // Mock the activity as embedded without additional TaskFragment layer in the task for + // simplicity. + doReturn(true).when(mActivity).isEmbedded(); + // Translucent Activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).build(); + doReturn(false).when(translucentActivity).matchParentBounds(); + doReturn(false).when(translucentActivity).fillsParent(); + mTask.addChild(translucentActivity); + // Check the strategy has not being applied + assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + } + + @Test + public void testTranslucentActivitiesDontGoInSizeCompactMode() { mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); setUpDisplaySizeWithApp(2800, 1400); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -524,7 +598,6 @@ public class SizeCompatTests extends WindowTestsBase { true /* ignoreOrientationRequest */); mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( 1.0f /*letterboxVerticalPositionMultiplier*/); - mActivity.nowVisible = true; prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); // We launch a transparent activity final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) @@ -557,7 +630,6 @@ public class SizeCompatTests extends WindowTestsBase { mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); setUpDisplaySizeWithApp(2800, 1400); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.nowVisible = true; prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); // Rotate to put activity in size compat mode. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java index 626ce8927158..55419a80b604 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java @@ -584,15 +584,34 @@ public final class UsbDescriptorParser { } /** + * Returns true only if there is a terminal whose subtype and terminal type are the same as + * the given values. * @hide */ - public boolean hasAudioTerminal(int subType) { + public boolean hasAudioTerminal(int subType, int terminalType) { for (UsbDescriptor descriptor : mDescriptors) { - if (descriptor instanceof UsbACInterface) { - if (((UsbACInterface) descriptor).getSubclass() - == UsbDescriptor.AUDIO_AUDIOCONTROL - && ((UsbACInterface) descriptor).getSubtype() - == subType) { + if (descriptor instanceof UsbACTerminal) { + if (((UsbACTerminal) descriptor).getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL + && ((UsbACTerminal) descriptor).getSubtype() == subType + && ((UsbACTerminal) descriptor).getTerminalType() == terminalType) { + return true; + } + } + } + return false; + } + + /** + * Returns true only if there is an interface whose subtype is the same as the given one and + * terminal type is different from the given one. + * @hide + */ + public boolean hasAudioTerminalExcludeType(int subType, int excludedTerminalType) { + for (UsbDescriptor descriptor : mDescriptors) { + if (descriptor instanceof UsbACTerminal) { + if (((UsbACTerminal) descriptor).getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL + && ((UsbACTerminal) descriptor).getSubtype() == subType + && ((UsbACTerminal) descriptor).getTerminalType() != excludedTerminalType) { return true; } } @@ -604,14 +623,21 @@ public final class UsbDescriptorParser { * @hide */ public boolean hasAudioPlayback() { - return hasAudioTerminal(UsbACInterface.ACI_OUTPUT_TERMINAL); + return hasAudioTerminalExcludeType( + UsbACInterface.ACI_OUTPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING) + && hasAudioTerminal( + UsbACInterface.ACI_INPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING); } /** * @hide */ public boolean hasAudioCapture() { - return hasAudioTerminal(UsbACInterface.ACI_INPUT_TERMINAL); + return hasAudioTerminalExcludeType( + UsbACInterface.ACI_INPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING) + && hasAudioTerminal( + UsbACInterface.ACI_OUTPUT_TERMINAL, + UsbTerminalTypes.TERMINAL_USB_STREAMING); } /** |