summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/multiuser.aconfig2
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java13
-rw-r--r--core/java/android/os/health/SystemHealthManager.java10
-rw-r--r--core/java/android/permission/flags.aconfig13
-rw-r--r--core/java/android/provider/Settings.java34
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig19
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java190
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java74
-rw-r--r--core/java/com/android/internal/policy/DecorView.java130
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java12
-rw-r--r--core/java/com/android/internal/widget/ActionBarOverlayLayout.java85
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig10
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt98
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt426
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt87
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt136
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java35
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h10
-rw-r--r--libs/hwui/jni/Graphics.cpp13
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java19
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt16
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt10
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt9
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt10
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt155
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt175
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt160
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt11
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt102
-rw-r--r--services/core/java/com/android/server/appop/AttributedOp.java11
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsRegistry.java56
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java11
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java8
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java11
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java19
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java16
-rw-r--r--services/core/java/com/android/server/power/ScreenUndimDetector.java44
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java42
-rw-r--r--services/core/java/com/android/server/wm/ActivitySnapshotController.java8
-rw-r--r--services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java84
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowListenerController.java6
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt14
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java10
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java27
-rw-r--r--tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt18
102 files changed, 2363 insertions, 956 deletions
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 5c904c15e706..7f4fd3eff57e 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -617,8 +617,8 @@ flag {
}
flag {
- namespace: "multi_user"
name: "logout_user_api"
+ namespace: "multiuser"
description: "Add API to logout user"
bug: "350045389"
}
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 0964cde5a1f4..c3ec96d17437 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -145,8 +145,17 @@ public final class MessageQueue {
}
if (Flags.forceConcurrentMessageQueue()) {
- sIsProcessAllowedToUseConcurrent = true;
- return;
+ // b/379472827: Robolectric tests use reflection to access MessageQueue.mMessages.
+ // This is a hack to allow Robolectric tests to use the legacy implementation.
+ try {
+ Class.forName("org.robolectric.Robolectric");
+ } catch (ClassNotFoundException e) {
+ // This is not a Robolectric test.
+ sIsProcessAllowedToUseConcurrent = true;
+ return;
+ }
+ // This is a Robolectric test.
+ // Continue to the following checks.
}
final String processName = Process.myProcessName();
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index a8a22f675e08..b82f278ef7d5 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -216,7 +216,7 @@ public class SystemHealthManager {
/**
* Gets the maximum number of TIDs this device supports for getting CPU headroom.
* <p>
- * See {@link CpuHeadroomParams#setTids(int...)}.
+ * See {@link CpuHeadroomParams.Builder#setTids(int...)}.
*
* @return the maximum size of TIDs supported
* @throws UnsupportedOperationException if the CPU headroom API is unsupported.
@@ -288,9 +288,7 @@ public class SystemHealthManager {
/**
* Gets the range of the calculation window size for CPU headroom.
* <p>
- * In API version 36, the range will be a superset of [50, 10000].
- * <p>
- * See {@link CpuHeadroomParams#setCalculationWindowMillis(int)}.
+ * See {@link CpuHeadroomParams.Builder#setCalculationWindowMillis(int)}.
*
* @return the range of the calculation window size supported in milliseconds.
* @throws UnsupportedOperationException if the CPU headroom API is unsupported.
@@ -310,9 +308,7 @@ public class SystemHealthManager {
/**
* Gets the range of the calculation window size for GPU headroom.
* <p>
- * In API version 36, the range will be a superset of [50, 10000].
- * <p>
- * See {@link GpuHeadroomParams#setCalculationWindowMillis(int)}.
+ * See {@link GpuHeadroomParams.Builder#setCalculationWindowMillis(int)}.
*
* @return the range of the calculation window size supported in milliseconds.
* @throws UnsupportedOperationException if the GPU headroom API is unsupported.
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 0476f62ec263..4bd54a173601 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -71,6 +71,19 @@ flag {
}
flag {
+ name: "unknown_call_setting_blocked_logging_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "enable the metrics when blocking certain app installs during an unknown call"
+ bug: "364535720"
+
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "op_enable_mobile_data_by_user"
is_exported: true
namespace: "permissions"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b97c9b5e83f2..f91056dbce30 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8651,6 +8651,34 @@ public final class Settings {
public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
/**
+ * Setting to indicate that content filters should be enabled on web browsers.
+ *
+ * <ul>
+ * <li>0 = Allow all sites
+ * <li>1 = Try to block explicit sites
+ * </ul>
+ *
+ * @hide
+ */
+ @Readable
+ public static final String BROWSER_CONTENT_FILTERS_ENABLED =
+ "browser_content_filters_enabled";
+
+ /**
+ * Setting to indicate that content filters should be enabled in web search engines.
+ *
+ * <ul>
+ * <li>0 = Off
+ * <li>1 = Filter
+ * </ul>
+ *
+ * @hide
+ */
+ @Readable
+ public static final String SEARCH_CONTENT_FILTERS_ENABLED =
+ "search_content_filters_enabled";
+
+ /**
* Set by the system to track if the user needs to see the call to action for
* the lockscreen notification policy.
* @hide
@@ -11132,6 +11160,12 @@ public final class Settings {
public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
/**
+ * Controls whether double tap to sleep is enabled.
+ * @hide
+ */
+ public static final String DOUBLE_TAP_TO_SLEEP = "double_tap_to_sleep";
+
+ /**
* The current assistant component. It could be a voice interaction service,
* or an activity that handles ACTION_ASSIST, or empty which means using the default
* handling.
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index b4fec416bd5f..3927c713e500 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -86,6 +86,14 @@ flag {
}
flag {
+ name: "action_mode_edge_to_edge"
+ namespace: "windowing_frontend"
+ description: "Make contextual action bar edge-to-edge"
+ bug: "379783298"
+ is_fixed_read_only: true
+}
+
+flag {
name: "keyguard_going_away_timeout"
namespace: "windowing_frontend"
description: "Allow a maximum of 10 seconds with keyguardGoingAway=true before force-resetting"
@@ -501,6 +509,17 @@ flag {
description: "Sets Launch powermode for activity launches earlier"
bug: "399380676"
is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "scramble_snapshot_file_name"
+ namespace: "windowing_frontend"
+ description: "Scramble the file name of task snapshot."
+ bug: "293139053"
+ is_fixed_read_only: true
metadata {
purpose: PURPOSE_BUGFIX
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 81ca23173457..c6207f9451c2 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -45,11 +45,13 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -203,15 +205,6 @@ public class BatteryStatsHistory {
BatteryHistoryFragment getLatestFragment();
/**
- * Given a fragment, returns the earliest fragment that follows it whose monotonic
- * start time falls within the specified range. `startTimeMs` is inclusive, `endTimeMs`
- * is exclusive.
- */
- @Nullable
- BatteryHistoryFragment getNextFragment(BatteryHistoryFragment current, long startTimeMs,
- long endTimeMs);
-
- /**
* Acquires a lock on the entire store.
*/
void lock();
@@ -268,6 +261,60 @@ public class BatteryStatsHistory {
void reset();
}
+ class BatteryHistoryParcelContainer {
+ private boolean mParcelReadyForReading;
+ private Parcel mParcel;
+ private BatteryStatsHistory.BatteryHistoryFragment mFragment;
+ private long mMonotonicStartTime;
+
+ BatteryHistoryParcelContainer(@NonNull Parcel parcel, long monotonicStartTime) {
+ mParcel = parcel;
+ mMonotonicStartTime = monotonicStartTime;
+ mParcelReadyForReading = true;
+ }
+
+ BatteryHistoryParcelContainer(@NonNull BatteryHistoryFragment fragment) {
+ mFragment = fragment;
+ mMonotonicStartTime = fragment.monotonicTimeMs;
+ mParcelReadyForReading = false;
+ }
+
+ @Nullable
+ Parcel getParcel() {
+ if (mParcelReadyForReading) {
+ return mParcel;
+ }
+
+ Parcel parcel = Parcel.obtain();
+ if (readFragmentToParcel(parcel, mFragment)) {
+ parcel.readInt(); // skip buffer size
+ mParcel = parcel;
+ } else {
+ parcel.recycle();
+ }
+ mParcelReadyForReading = true;
+ return mParcel;
+ }
+
+ long getMonotonicStartTime() {
+ return mMonotonicStartTime;
+ }
+
+ /**
+ * Recycles the parcel (if appropriate). Should be called after the parcel has been
+ * processed by the iterator.
+ */
+ void close() {
+ if (mParcel != null && mFragment != null) {
+ mParcel.recycle();
+ }
+ // ParcelContainers are not meant to be reusable. To prevent any unintentional
+ // access to the parcel after it has been recycled, clear the references to it.
+ mParcel = null;
+ mFragment = null;
+ }
+ }
+
private final Parcel mHistoryBuffer;
private final HistoryStepDetailsCalculator mStepDetailsCalculator;
private final Clock mClock;
@@ -447,6 +494,7 @@ public class BatteryStatsHistory {
mWritableHistory = writableHistory;
if (mWritableHistory != null) {
mMutable = false;
+ mHistoryBufferStartTime = mWritableHistory.mHistoryBufferStartTime;
mHistoryMonotonicEndTime = mWritableHistory.mHistoryMonotonicEndTime;
}
@@ -689,95 +737,66 @@ public class BatteryStatsHistory {
}
/**
- * When iterating history files and history buffer, always start from the lowest numbered
- * history file, when reached the mActiveFile (highest numbered history file), do not read from
- * mActiveFile, read from history buffer instead because the buffer has more updated data.
- *
- * @return The parcel that has next record. null if finished all history files and history
- * buffer
+ * Returns all chunks of accumulated history that contain items within the range between
+ * `startTimeMs` (inclusive) and `endTimeMs` (exclusive)
*/
- @Nullable
- public Parcel getNextParcel(long startTimeMs, long endTimeMs) {
- checkImmutable();
+ Queue<BatteryHistoryParcelContainer> getParcelContainers(long startTimeMs, long endTimeMs) {
+ if (mMutable) {
+ throw new IllegalStateException("Iterating over a mutable battery history");
+ }
- // First iterate through all records in current parcel.
- if (mCurrentParcel != null) {
- if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
- // There are more records in current parcel.
- return mCurrentParcel;
- } else if (mHistoryBuffer == mCurrentParcel) {
- // finished iterate through all history files and history buffer.
- return null;
- } else if (mHistoryParcels == null
- || !mHistoryParcels.contains(mCurrentParcel)) {
- // current parcel is from history file.
- mCurrentParcel.recycle();
- }
+ if (endTimeMs == MonotonicClock.UNDEFINED || endTimeMs == 0) {
+ endTimeMs = Long.MAX_VALUE;
}
+ Queue<BatteryHistoryParcelContainer> containers = new ArrayDeque<>();
+
if (mStore != null) {
- BatteryHistoryFragment next = mStore.getNextFragment(mCurrentFragment, startTimeMs,
- endTimeMs);
- while (next != null) {
- mCurrentParcel = null;
- mCurrentParcelEnd = 0;
- final Parcel p = Parcel.obtain();
- if (readFragmentToParcel(p, next)) {
- int bufSize = p.readInt();
- int curPos = p.dataPosition();
- mCurrentParcelEnd = curPos + bufSize;
- mCurrentParcel = p;
- if (curPos < mCurrentParcelEnd) {
- mCurrentFragment = next;
- return mCurrentParcel;
- }
- } else {
- p.recycle();
+ List<BatteryHistoryFragment> fragments = mStore.getFragments();
+ for (int i = 0; i < fragments.size(); i++) {
+ BatteryHistoryFragment fragment = fragments.get(i);
+ if (fragment.monotonicTimeMs >= endTimeMs) {
+ break;
+ }
+
+ if (fragment.monotonicTimeMs >= startTimeMs && fragment != mActiveFragment) {
+ containers.add(new BatteryHistoryParcelContainer(fragment));
}
- next = mStore.getNextFragment(next, startTimeMs, endTimeMs);
}
}
- // mHistoryParcels is created when BatteryStatsImpl object is created from deserialization
- // of a parcel, such as Settings app or checkin file.
if (mHistoryParcels != null) {
- while (mParcelIndex < mHistoryParcels.size()) {
- final Parcel p = mHistoryParcels.get(mParcelIndex++);
+ for (int i = 0; i < mHistoryParcels.size(); i++) {
+ final Parcel p = mHistoryParcels.get(i);
if (!verifyVersion(p)) {
continue;
}
- // skip monotonic time field.
- p.readLong();
- // skip monotonic end time field
- p.readLong();
+
+ long monotonicStartTime = p.readLong();
+ if (monotonicStartTime >= endTimeMs) {
+ continue;
+ }
+
+ long monotonicEndTime = p.readLong();
+ if (monotonicEndTime < startTimeMs) {
+ continue;
+ }
+
// skip monotonic size field
p.readLong();
+ // skip buffer size field
+ p.readInt();
- final int bufSize = p.readInt();
- final int curPos = p.dataPosition();
- mCurrentParcelEnd = curPos + bufSize;
- mCurrentParcel = p;
- if (curPos < mCurrentParcelEnd) {
- return mCurrentParcel;
- }
+ containers.add(new BatteryHistoryParcelContainer(p, monotonicStartTime));
}
}
- // finished iterator through history files (except the last one), now history buffer.
- if (mHistoryBuffer.dataSize() <= 0) {
- // buffer is empty.
- return null;
- }
- mHistoryBuffer.setDataPosition(0);
- mCurrentParcel = mHistoryBuffer;
- mCurrentParcelEnd = mCurrentParcel.dataSize();
- return mCurrentParcel;
- }
-
- private void checkImmutable() {
- if (mMutable) {
- throw new IllegalStateException("Iterating over a mutable battery history");
+ if (mHistoryBufferStartTime < endTimeMs) {
+ mHistoryBuffer.setDataPosition(0);
+ containers.add(
+ new BatteryHistoryParcelContainer(mHistoryBuffer, mHistoryBufferStartTime));
}
+ return containers;
}
/**
@@ -818,21 +837,6 @@ public class BatteryStatsHistory {
}
/**
- * Extracts the monotonic time, as per {@link MonotonicClock}, from the supplied battery history
- * buffer.
- */
- public long getHistoryBufferStartTime(Parcel p) {
- int pos = p.dataPosition();
- p.setDataPosition(0);
- p.readInt(); // Skip the version field
- long monotonicTime = p.readLong();
- p.readLong(); // Skip monotonic end time field
- p.readLong(); // Skip monotonic size field
- p.setDataPosition(pos);
- return monotonicTime;
- }
-
- /**
* Writes the battery history contents for persistence.
*/
public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 38398b4bf6f0..09f9b0bf8c7e 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -24,6 +24,7 @@ import android.util.Slog;
import android.util.SparseArray;
import java.util.Iterator;
+import java.util.Queue;
/**
* An iterator for {@link BatteryStats.HistoryItem}'s.
@@ -48,7 +49,10 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
private long mBaseMonotonicTime;
private long mBaseTimeUtc;
private int mItemIndex = 0;
- private int mMaxHistoryItems;
+ private final int mMaxHistoryItems;
+ private int mParcelDataPosition;
+
+ private Queue<BatteryStatsHistory.BatteryHistoryParcelContainer> mParcelContainers;
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
long endTimeMs) {
@@ -62,7 +66,11 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
@Override
public boolean hasNext() {
if (!mNextItemReady) {
- advance();
+ if (!advance()) {
+ mHistoryItem = null;
+ close();
+ }
+ mNextItemReady = true;
}
return mHistoryItem != null;
@@ -75,35 +83,48 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
@Override
public BatteryStats.HistoryItem next() {
if (!mNextItemReady) {
- advance();
+ if (!advance()) {
+ mHistoryItem = null;
+ close();
+ }
}
mNextItemReady = false;
return mHistoryItem;
}
- private void advance() {
- while (true) {
- if (mItemIndex > mMaxHistoryItems) {
- Slog.wtfStack(TAG, "Number of battery history items is too large: " + mItemIndex);
- break;
- }
+ private boolean advance() {
+ if (mParcelContainers == null) {
+ mParcelContainers = mBatteryStatsHistory.getParcelContainers(mStartTimeMs, mEndTimeMs);
+ }
- Parcel p = mBatteryStatsHistory.getNextParcel(mStartTimeMs, mEndTimeMs);
- if (p == null) {
- break;
+ BatteryStatsHistory.BatteryHistoryParcelContainer container;
+ while ((container = mParcelContainers.peek()) != null) {
+ Parcel p = container.getParcel();
+ if (p == null || p.dataPosition() >= p.dataSize()) {
+ container.close();
+ mParcelContainers.remove();
+ mParcelDataPosition = 0;
+ continue;
}
if (!mTimeInitialized) {
- mBaseMonotonicTime = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+ mBaseMonotonicTime = container.getMonotonicStartTime();
mHistoryItem.time = mBaseMonotonicTime;
mTimeInitialized = true;
}
try {
readHistoryDelta(p, mHistoryItem);
+ int dataPosition = p.dataPosition();
+ if (dataPosition <= mParcelDataPosition) {
+ Slog.wtf(TAG, "Corrupted battery history, parcel is not progressing: "
+ + dataPosition + " of " + p.dataSize());
+ return false;
+ }
+ mParcelDataPosition = dataPosition;
} catch (Throwable t) {
Slog.wtf(TAG, "Corrupted battery history", t);
- break;
+ return false;
}
if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
@@ -111,21 +132,24 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime);
}
- mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime);
+ if (mHistoryItem.time < mStartTimeMs) {
+ continue;
+ }
- if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) {
- break;
+ if (mEndTimeMs != 0 && mEndTimeMs != MonotonicClock.UNDEFINED
+ && mHistoryItem.time >= mEndTimeMs) {
+ return false;
}
- if (mHistoryItem.time >= mStartTimeMs) {
- mItemIndex++;
- mNextItemReady = true;
- return;
+
+ if (mItemIndex++ > mMaxHistoryItems) {
+ Slog.wtfStack(TAG, "Number of battery history items is too large: " + mItemIndex);
+ return false;
}
- }
- mHistoryItem = null;
- mNextItemReady = true;
- close();
+ mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime);
+ return true;
+ }
+ return false;
}
private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e20a52b24485..3d81e4fc7acd 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -120,6 +120,7 @@ import com.android.internal.view.menu.MenuHelper;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
+import com.android.window.flags.Flags;
import java.util.List;
import java.util.concurrent.Executor;
@@ -1003,7 +1004,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
public void onWindowSystemUiVisibilityChanged(int visible) {
updateColorViews(null /* insets */, true /* animate */);
- if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
+ if (!Flags.actionModeEdgeToEdge()
+ && mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
updateStatusGuardColor();
}
}
@@ -1040,7 +1042,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
insets = updateColorViews(insets, true /* animate */);
- insets = updateStatusGuard(insets);
+ insets = updateActionModeInsets(insets);
if (getForeground() != null) {
drawableChanged();
}
@@ -1592,7 +1594,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
- private WindowInsets updateStatusGuard(WindowInsets insets) {
+ private WindowInsets updateActionModeInsets(WindowInsets insets) {
boolean showStatusGuard = false;
// Show the status guard when the non-overlay contextual action bar is showing
if (mPrimaryActionModeView != null) {
@@ -1608,54 +1610,78 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
final Rect rect = mTempRect;
// Apply the insets that have not been applied by the contentParent yet.
- WindowInsets innerInsets =
+ final WindowInsets innerInsets =
mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
- int newTopMargin = innerInsets.getSystemWindowInsetTop();
- int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
- int newRightMargin = innerInsets.getSystemWindowInsetRight();
-
- // Must use root window insets for the guard, because the color views consume
- // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
- // the status guard is attached at the root.
- WindowInsets rootInsets = getRootWindowInsets();
- int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
- int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
-
- if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
- || mlp.rightMargin != newRightMargin) {
- mlpChanged = true;
- mlp.topMargin = newTopMargin;
- mlp.leftMargin = newLeftMargin;
- mlp.rightMargin = newRightMargin;
- }
+ final boolean consumesSystemWindowInsetsTop;
+ if (Flags.actionModeEdgeToEdge()) {
+ final Insets newPadding = innerInsets.getSystemWindowInsets();
+ final Insets newMargin = innerInsets.getInsets(
+ WindowInsets.Type.navigationBars());
+
+ // Don't extend into navigation bar area so the width can align with status
+ // bar color view.
+ if (mlp.leftMargin != newMargin.left
+ || mlp.rightMargin != newMargin.right) {
+ mlpChanged = true;
+ mlp.leftMargin = newMargin.left;
+ mlp.rightMargin = newMargin.right;
+ }
+
+ mPrimaryActionModeView.setPadding(
+ newPadding.left - newMargin.left,
+ newPadding.top,
+ newPadding.right - newMargin.right,
+ 0);
+ consumesSystemWindowInsetsTop = newPadding.top > 0;
+ } else {
+ int newTopMargin = innerInsets.getSystemWindowInsetTop();
+ int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
+ int newRightMargin = innerInsets.getSystemWindowInsetRight();
+
+ // Must use root window insets for the guard, because the color views
+ // consume the navigation bar inset if the window does not request
+ // LAYOUT_HIDE_NAV - but the status guard is attached at the root.
+ WindowInsets rootInsets = getRootWindowInsets();
+ int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
+ int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
+
+ if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
+ || mlp.rightMargin != newRightMargin) {
+ mlpChanged = true;
+ mlp.topMargin = newTopMargin;
+ mlp.leftMargin = newLeftMargin;
+ mlp.rightMargin = newRightMargin;
+ }
- if (newTopMargin > 0 && mStatusGuard == null) {
- mStatusGuard = new View(mContext);
- mStatusGuard.setVisibility(GONE);
- final LayoutParams lp = new LayoutParams(MATCH_PARENT,
- mlp.topMargin, Gravity.LEFT | Gravity.TOP);
- lp.leftMargin = newGuardLeftMargin;
- lp.rightMargin = newGuardRightMargin;
- addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
- } else if (mStatusGuard != null) {
- final LayoutParams lp = (LayoutParams)
- mStatusGuard.getLayoutParams();
- if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
- || lp.rightMargin != newGuardRightMargin) {
- lp.height = mlp.topMargin;
+ if (newTopMargin > 0 && mStatusGuard == null) {
+ mStatusGuard = new View(mContext);
+ mStatusGuard.setVisibility(GONE);
+ final LayoutParams lp = new LayoutParams(MATCH_PARENT,
+ mlp.topMargin, Gravity.LEFT | Gravity.TOP);
lp.leftMargin = newGuardLeftMargin;
lp.rightMargin = newGuardRightMargin;
- mStatusGuard.setLayoutParams(lp);
+ addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
+ } else if (mStatusGuard != null) {
+ final LayoutParams lp = (LayoutParams)
+ mStatusGuard.getLayoutParams();
+ if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
+ || lp.rightMargin != newGuardRightMargin) {
+ lp.height = mlp.topMargin;
+ lp.leftMargin = newGuardLeftMargin;
+ lp.rightMargin = newGuardRightMargin;
+ mStatusGuard.setLayoutParams(lp);
+ }
}
- }
- // The action mode's theme may differ from the app, so
- // always show the status guard above it if we have one.
- showStatusGuard = mStatusGuard != null;
+ // The action mode's theme may differ from the app, so
+ // always show the status guard above it if we have one.
+ showStatusGuard = mStatusGuard != null;
- if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
- // If it wasn't previously shown, the color may be stale
- updateStatusGuardColor();
+ if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
+ // If it wasn't previously shown, the color may be stale
+ updateStatusGuardColor();
+ }
+ consumesSystemWindowInsetsTop = showStatusGuard;
}
// We only need to consume the insets if the action
@@ -1664,14 +1690,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// screen_simple_overlay_action_mode.xml).
final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
& (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
- if (nonOverlay && showStatusGuard) {
+ if (nonOverlay && consumesSystemWindowInsetsTop) {
insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
}
} else {
- // reset top margin
- if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
- mlpChanged = true;
- mlp.topMargin = 0;
+ if (!Flags.actionModeEdgeToEdge()) {
+ // reset top margin
+ if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
+ mlpChanged = true;
+ mlp.topMargin = 0;
+ }
}
}
if (mlpChanged) {
@@ -1679,7 +1707,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
}
- if (mStatusGuard != null) {
+ if (!Flags.actionModeEdgeToEdge() && mStatusGuard != null) {
mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
}
return insets;
@@ -2183,7 +2211,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
for (int i = getChildCount() - 1; i >= 0; i--) {
View v = getChildAt(i);
if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
- && v != mStatusGuard) {
+ && (Flags.actionModeEdgeToEdge() || v != mStatusGuard)) {
removeViewAt(i);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 80fc218839d5..d5bb51187ba4 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -34,6 +34,7 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
+import com.android.window.flags.Flags;
/**
* @hide
@@ -315,12 +316,14 @@ public class ActionBarContextView extends AbsActionBarView {
final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
- int maxHeight = mContentHeight > 0 ?
- mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+ final int maxHeight = !Flags.actionModeEdgeToEdge() && mContentHeight > 0
+ ? mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
final int verticalPadding = getPaddingTop() + getPaddingBottom();
int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
- final int height = maxHeight - verticalPadding;
+ final int height = Flags.actionModeEdgeToEdge()
+ ? mContentHeight > 0 ? mContentHeight : maxHeight
+ : maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
if (mClose != null) {
@@ -376,7 +379,8 @@ public class ActionBarContextView extends AbsActionBarView {
}
setMeasuredDimension(contentWidth, measuredHeight);
} else {
- setMeasuredDimension(contentWidth, maxHeight);
+ setMeasuredDimension(contentWidth, Flags.actionModeEdgeToEdge()
+ ? mContentHeight + verticalPadding : maxHeight);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index ff57fd4fe2ce..362b79db4003 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -294,54 +294,24 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
}
}
- private boolean applyInsets(View view, Rect insets, boolean toPadding,
- boolean left, boolean top, boolean right, boolean bottom) {
- boolean changed;
- if (toPadding) {
- changed = setMargin(view, EMPTY_RECT, left, top, right, bottom);
- changed |= setPadding(view, insets, left, top, right, bottom);
- } else {
- changed = setPadding(view, EMPTY_RECT, left, top, right, bottom);
- changed |= setMargin(view, insets, left, top, right, bottom);
- }
- return changed;
- }
-
- private boolean setPadding(View view, Rect insets,
- boolean left, boolean top, boolean right, boolean bottom) {
- if ((left && view.getPaddingLeft() != insets.left)
- || (top && view.getPaddingTop() != insets.top)
- || (right && view.getPaddingRight() != insets.right)
- || (bottom && view.getPaddingBottom() != insets.bottom)) {
- view.setPadding(
- left ? insets.left : view.getPaddingLeft(),
- top ? insets.top : view.getPaddingTop(),
- right ? insets.right : view.getPaddingRight(),
- bottom ? insets.bottom : view.getPaddingBottom());
- return true;
- }
- return false;
- }
-
- private boolean setMargin(View view, Rect insets,
- boolean left, boolean top, boolean right, boolean bottom) {
+ private boolean setMargin(View view, int left, int top, int right, int bottom) {
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
boolean changed = false;
- if (left && lp.leftMargin != insets.left) {
+ if (lp.leftMargin != left) {
changed = true;
- lp.leftMargin = insets.left;
+ lp.leftMargin = left;
}
- if (top && lp.topMargin != insets.top) {
+ if (lp.topMargin != top) {
changed = true;
- lp.topMargin = insets.top;
+ lp.topMargin = top;
}
- if (right && lp.rightMargin != insets.right) {
+ if (lp.rightMargin != right) {
changed = true;
- lp.rightMargin = insets.right;
+ lp.rightMargin = right;
}
- if (bottom && lp.bottomMargin != insets.bottom) {
+ if (lp.bottomMargin != bottom) {
changed = true;
- lp.bottomMargin = insets.bottom;
+ lp.bottomMargin = bottom;
}
return changed;
}
@@ -367,12 +337,30 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
final Insets sysInsets = insets.getSystemWindowInsets();
mSystemInsets.set(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom);
- // The top and bottom action bars are always within the content area.
- boolean changed = applyInsets(mActionBarTop, mSystemInsets,
- mActionBarExtendsIntoSystemInsets, true, true, true, false);
- if (mActionBarBottom != null) {
- changed |= applyInsets(mActionBarBottom, mSystemInsets,
- mActionBarExtendsIntoSystemInsets, true, false, true, true);
+ boolean changed = false;
+ if (mActionBarExtendsIntoSystemInsets) {
+ // Don't extend into navigation bar area so the width can align with status bar
+ // color view.
+ final Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
+ final int paddingLeft = sysInsets.left - navBarInsets.left;
+ final int paddingRight = sysInsets.right - navBarInsets.right;
+ mActionBarTop.setPadding(paddingLeft, sysInsets.top, paddingRight, 0);
+ changed |= setMargin(
+ mActionBarTop, navBarInsets.left, 0, navBarInsets.right, 0);
+ if (mActionBarBottom != null) {
+ mActionBarBottom.setPadding(paddingLeft, 0, paddingRight, sysInsets.bottom);
+ changed |= setMargin(
+ mActionBarBottom, navBarInsets.left, 0, navBarInsets.right, 0);
+ }
+ } else {
+ mActionBarTop.setPadding(0, 0, 0, 0);
+ changed |= setMargin(
+ mActionBarTop, sysInsets.left, sysInsets.top, sysInsets.right, 0);
+ if (mActionBarBottom != null) {
+ mActionBarBottom.setPadding(0, 0, 0, 0);
+ changed |= setMargin(
+ mActionBarTop, sysInsets.left, 0, sysInsets.right, sysInsets.bottom);
+ }
}
// Cannot use the result of computeSystemWindowInsets, because that consumes the
@@ -521,7 +509,12 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
);
}
}
- setMargin(mContent, mContentInsets, true, true, true, true);
+ setMargin(
+ mContent,
+ mContentInsets.left,
+ mContentInsets.top,
+ mContentInsets.right,
+ mContentInsets.bottom);
if (!mLastInnerInsets.equals(mInnerInsets)) {
// If the inner insets have changed, we need to dispatch this down to
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9773f557dfaa..8c5b20da73ef 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2812,6 +2812,20 @@
<integer name="config_dreamsBatteryLevelDrainCutoff">5</integer>
<!-- Limit of how long the device can remain unlocked due to attention checking. -->
<integer name="config_attentionMaximumExtension">900000</integer> <!-- 15 minutes. -->
+
+ <!-- Enables or disables the 'prevent screen timeout' feature, where when a user manually
+ undims the screen, the feature acquires a wakelock to prevent screen timeout.
+ false = disabled, true = enabled. Disabled by default. -->
+ <bool name="config_defaultPreventScreenTimeoutEnabled">false</bool>
+ <!-- Default value (in milliseconds) to prevent the screen timeout after user undims it
+ manually between screen dims, a sign the user is interacting with the device. -->
+ <integer name="config_defaultPreventScreenTimeoutForMillis">300000</integer> <!-- 5 minutes. -->
+ <!-- Default max duration (in milliseconds) of the time between undims to still consider them
+ consecutive. -->
+ <integer name="config_defaultMaxDurationBetweenUndimsMillis">600000</integer> <!-- 10 min. -->
+ <!-- Default number of user undims required to trigger preventing screen sleep. -->
+ <integer name="config_defaultUndimsRequired">2</integer>
+
<!-- Whether there is to be a chosen Dock User who is the only user allowed to dream. -->
<bool name="config_dreamsOnlyEnabledForDockUser">false</bool>
<!-- Whether dreams are disabled when ambient mode is suppressed. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2b7261b62c64..c87ab283e188 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4442,6 +4442,11 @@
<!-- For Attention Service -->
<java-symbol type="integer" name="config_attentionMaximumExtension" />
+ <java-symbol type="bool" name="config_defaultPreventScreenTimeoutEnabled" />
+ <java-symbol type="integer" name="config_defaultPreventScreenTimeoutForMillis" />
+ <java-symbol type="integer" name="config_defaultMaxDurationBetweenUndimsMillis" />
+ <java-symbol type="integer" name="config_defaultUndimsRequired" />
+
<java-symbol type="string" name="config_incidentReportApproverPackage" />
<java-symbol type="array" name="config_restrictedImagesServices" />
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 1e72d64397d7..ab2804626361 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -194,3 +194,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_gsf"
+ namespace: "multitasking"
+ description: "Applies GSF font styles to multitasking."
+ bug: "400534660"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
index 2bb6cf4ec3aa..73277310ffe4 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
+import android.util.TypedValue
import android.view.View
import com.android.wm.shell.shared.R
@@ -37,14 +38,21 @@ class DropTargetView(context: Context) : View(context) {
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
style = Paint.Style.STROKE
- strokeWidth = context.resources.getDimensionPixelSize(R.dimen.drop_target_stroke).toFloat()
+ strokeWidth = 1.dpToPx()
}
- private val cornerRadius = context.resources.getDimensionPixelSize(
- R.dimen.drop_target_radius).toFloat()
+ private val cornerRadius = 28.dpToPx()
private val rect = RectF(0f, 0f, 0f, 0f)
+ // TODO b/396539130: Use shared xml resources once we can access them in launcher
+ private fun Int.dpToPx() =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ this.toFloat(),
+ context.resources.displayMetrics
+ )
+
override fun onDraw(canvas: Canvas) {
canvas.save()
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, rectPaint)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index e7e7be9cdf6b..d5f2dbdbf5f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -159,19 +159,22 @@ public class BubbleTransitions {
private final WindowContainerTransaction mPendingWct;
private final boolean mReleasedOnLeft;
private final float mTaskScale;
+ private final float mCornerRadius;
private final PointF mDragPosition;
/**
* @param releasedOnLeft true if the bubble was released in the left drop target
* @param taskScale the scale of the task when it was dragged to bubble
+ * @param cornerRadius the corner radius of the task when it was dragged to bubble
* @param dragPosition the position of the task when it was dragged to bubble
* @param wct pending operations to be applied when finishing the drag
*/
- public DragData(boolean releasedOnLeft, float taskScale, @Nullable PointF dragPosition,
- @Nullable WindowContainerTransaction wct) {
+ public DragData(boolean releasedOnLeft, float taskScale, float cornerRadius,
+ @Nullable PointF dragPosition, @Nullable WindowContainerTransaction wct) {
mPendingWct = wct;
mReleasedOnLeft = releasedOnLeft;
mTaskScale = taskScale;
+ mCornerRadius = cornerRadius;
mDragPosition = dragPosition != null ? dragPosition : new PointF(0, 0);
}
@@ -198,6 +201,13 @@ public class BubbleTransitions {
}
/**
+ * @return the corner radius of the task when it was dragged to bubble
+ */
+ public float getCornerRadius() {
+ return mCornerRadius;
+ }
+
+ /**
* @return position of the task when it was dragged to bubble
*/
public PointF getDragPosition() {
@@ -362,6 +372,7 @@ public class BubbleTransitions {
(int) mDragData.getDragPosition().y);
startTransaction.setScale(mSnapshot, mDragData.getTaskScale(),
mDragData.getTaskScale());
+ startTransaction.setCornerRadius(mSnapshot, mDragData.getCornerRadius());
}
// Now update state (and talk to launcher) in parallel with snapshot stuff
@@ -377,12 +388,6 @@ public class BubbleTransitions {
startTransaction.setPosition(mSnapshot, left, top);
startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
- BubbleBarExpandedView bbev = mBubble.getBubbleBarExpandedView();
- if (bbev != null) {
- // Corners get reset during the animation. Add them back
- startTransaction.setCornerRadius(mSnapshot, bbev.getRestingCornerRadius());
- }
-
startTransaction.apply();
mTaskViewTransitions.onExternalDone(transition);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 214e6ad455a1..aeef211ae3f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -143,6 +143,9 @@ public class PipBoundsState {
*/
public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect();
+ private final List<OnPipComponentChangedListener> mOnPipComponentChangedListeners =
+ new ArrayList<>();
+
// the size of the current bounds relative to the max size spec
private float mBoundsScale;
@@ -156,9 +159,7 @@ public class PipBoundsState {
// Update the relative proportion of the bounds compared to max possible size. Max size
// spec takes the aspect ratio of the bounds into account, so both width and height
// scale by the same factor.
- addPipExclusionBoundsChangeCallback((bounds) -> {
- updateBoundsScale();
- });
+ addPipExclusionBoundsChangeCallback((bounds) -> updateBoundsScale());
}
/** Reloads the resources. */
@@ -341,11 +342,14 @@ public class PipBoundsState {
/** Set the last {@link ComponentName} to enter PIP mode. */
public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) {
final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
+ if (!changed) return;
+ clearReentryState();
+ setHasUserResizedPip(false);
+ setHasUserMovedPip(false);
+ final ComponentName oldComponentName = mLastPipComponentName;
mLastPipComponentName = lastPipComponentName;
- if (changed) {
- clearReentryState();
- setHasUserResizedPip(false);
- setHasUserMovedPip(false);
+ for (OnPipComponentChangedListener listener : mOnPipComponentChangedListeners) {
+ listener.onPipComponentChanged(oldComponentName, mLastPipComponentName);
}
}
@@ -616,6 +620,21 @@ public class PipBoundsState {
}
}
+ /** Adds callback to listen on component change. */
+ public void addOnPipComponentChangedListener(@NonNull OnPipComponentChangedListener listener) {
+ if (!mOnPipComponentChangedListeners.contains(listener)) {
+ mOnPipComponentChangedListeners.add(listener);
+ }
+ }
+
+ /** Removes callback to listen on component change. */
+ public void removeOnPipComponentChangedListener(
+ @NonNull OnPipComponentChangedListener listener) {
+ if (mOnPipComponentChangedListeners.contains(listener)) {
+ mOnPipComponentChangedListeners.remove(listener);
+ }
+ }
+
public LauncherState getLauncherState() {
return mLauncherState;
}
@@ -695,7 +714,7 @@ public class PipBoundsState {
* Represents the state of pip to potentially restore upon reentry.
*/
@VisibleForTesting
- public static final class PipReentryState {
+ static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
private final float mSnapFraction;
@@ -722,6 +741,22 @@ public class PipBoundsState {
}
}
+ /**
+ * Listener interface for PiP component change, i.e. the app in pip mode changes
+ * TODO: Move this out of PipBoundsState once pip1 is deprecated.
+ */
+ public interface OnPipComponentChangedListener {
+ /**
+ * Callback when the component in pip mode changes.
+ * @param oldPipComponent previous component in pip mode,
+ * {@code null} if this is the very first time PiP appears.
+ * @param newPipComponent new component that enters pip mode.
+ */
+ void onPipComponentChanged(
+ @Nullable ComponentName oldPipComponent,
+ @NonNull ComponentName newPipComponent);
+ }
+
/** Dumps internal state. */
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
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 e20a3d839def..fec1f56c76bb 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
@@ -1466,11 +1466,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// Freeze the configuration size with offset to prevent app get a configuration
// changed or relaunch. This is required to make sure client apps will calculate
// insets properly after layout shifted.
- if (mTargetYOffset == 0) {
- mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
- } else {
- mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this);
- }
+ mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this);
}
// Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 423d1750de75..f959683701d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -451,7 +451,8 @@ public abstract class WMShellModule {
Optional<DesktopImmersiveController> desktopImmersiveController,
WindowDecorViewModel windowDecorViewModel,
Optional<TaskChangeListener> taskChangeListener,
- FocusTransitionObserver focusTransitionObserver) {
+ FocusTransitionObserver focusTransitionObserver,
+ Optional<DesksTransitionObserver> desksTransitionObserver) {
return new FreeformTaskTransitionObserver(
context,
shellInit,
@@ -459,7 +460,8 @@ public abstract class WMShellModule {
desktopImmersiveController,
windowDecorViewModel,
taskChangeListener,
- focusTransitionObserver);
+ focusTransitionObserver,
+ desksTransitionObserver);
}
@WMSingleton
@@ -772,7 +774,7 @@ public abstract class WMShellModule {
Optional<BubbleController> bubbleController,
OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
DesksOrganizer desksOrganizer,
- DesksTransitionObserver desksTransitionObserver,
+ Optional<DesksTransitionObserver> desksTransitionObserver,
UserProfileContexts userProfileContexts,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DragToDisplayTransitionHandler dragToDisplayTransitionHandler,
@@ -813,7 +815,7 @@ public abstract class WMShellModule {
bubbleController,
overviewToDesktopTransitionObserver,
desksOrganizer,
- desksTransitionObserver,
+ desksTransitionObserver.get(),
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -874,6 +876,7 @@ public abstract class WMShellModule {
Transitions transitions,
@DynamicOverride DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer shellTaskOrganizer,
+ DesksOrganizer desksOrganizer,
InteractionJankMonitor interactionJankMonitor,
@ShellMainThread Handler handler) {
int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
@@ -886,6 +889,7 @@ public abstract class WMShellModule {
transitions,
desktopUserRepositories,
shellTaskOrganizer,
+ desksOrganizer,
maxTaskLimit <= 0 ? null : maxTaskLimit,
interactionJankMonitor,
context,
@@ -1215,7 +1219,6 @@ public abstract class WMShellModule {
Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
Optional<BackAnimationController> backAnimationController,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
- @NonNull DesksTransitionObserver desksTransitionObserver,
ShellInit shellInit) {
return desktopUserRepositories.flatMap(
repository ->
@@ -1228,17 +1231,21 @@ public abstract class WMShellModule {
desktopMixedTransitionHandler.get(),
backAnimationController.get(),
desktopWallpaperActivityTokenProvider,
- desksTransitionObserver,
shellInit)));
}
@WMSingleton
@Provides
- static DesksTransitionObserver provideDesksTransitionObserver(
- @NonNull @DynamicOverride DesktopUserRepositories desktopUserRepositories,
+ static Optional<DesksTransitionObserver> provideDesksTransitionObserver(
+ Context context,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
@NonNull DesksOrganizer desksOrganizer
) {
- return new DesksTransitionObserver(desktopUserRepositories, desksOrganizer);
+ if (DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.of(
+ new DesksTransitionObserver(desktopUserRepositories, desksOrganizer));
+ }
+ return Optional.empty();
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index bbb300ea42da..f64bd757de3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -235,6 +235,10 @@ class DesktopRepository(
/** Returns the default desk in the given display. */
private fun getDefaultDesk(displayId: Int): Desk? = desktopData.getDefaultDesk(displayId)
+ /** Returns whether the given desk is active in its display. */
+ fun isDeskActive(deskId: Int): Boolean =
+ desktopData.getAllActiveDesks().any { desk -> desk.deskId == deskId }
+
/** Sets the given desk as the active one in the given display. */
fun setActiveDesk(displayId: Int, deskId: Int) {
logD("setActiveDesk for displayId=%d and deskId=%d", displayId, deskId)
@@ -485,7 +489,7 @@ class DesktopRepository(
fun getExpandedTasksOrdered(displayId: Int): List<Int> =
getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
- @VisibleForTesting
+ /** Returns all active non-minimized tasks for [deskId] ordered from top to bottom. */
fun getExpandedTasksIdsInDeskOrdered(deskId: Int): List<Int> =
getFreeformTasksIdsInDeskInZOrder(deskId).filter { !isMinimizedTask(it) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 301b79afd537..f67323bb7eb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -48,6 +48,7 @@ import android.view.DragEvent
import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
+import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_NONE
@@ -961,10 +962,13 @@ class DesktopTasksController(
.apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
.toBundle(),
)
+ val deskId = taskRepository.getDeskIdForTask(taskId) ?: getDefaultDeskId(DEFAULT_DISPLAY)
startLaunchTransition(
TRANSIT_OPEN,
wct,
taskId,
+ deskId = deskId,
+ displayId = DEFAULT_DISPLAY,
remoteTransition = remoteTransition,
unminimizeReason = unminimizeReason,
)
@@ -982,19 +986,26 @@ class DesktopTasksController(
remoteTransition: RemoteTransition? = null,
unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
) {
- logV("moveTaskToFront taskId=%s", taskInfo.taskId)
+ val deskId =
+ taskRepository.getDeskIdForTask(taskInfo.taskId) ?: getDefaultDeskId(taskInfo.displayId)
+ logV("moveTaskToFront taskId=%s deskId=%s", taskInfo.taskId, deskId)
// If a task is tiled, another task should be brought to foreground with it so let
// tiling controller handle the request.
if (snapEventHandler.moveTaskToFrontIfTiled(taskInfo)) {
return
}
val wct = WindowContainerTransaction()
- wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.reorderTaskToFront(wct, deskId, taskInfo)
+ } else {
+ wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
+ }
startLaunchTransition(
transitionType = TRANSIT_TO_FRONT,
wct = wct,
launchingTaskId = taskInfo.taskId,
remoteTransition = remoteTransition,
+ deskId = deskId,
displayId = taskInfo.displayId,
unminimizeReason = unminimizeReason,
)
@@ -1006,14 +1017,22 @@ class DesktopTasksController(
wct: WindowContainerTransaction,
launchingTaskId: Int?,
remoteTransition: RemoteTransition? = null,
- displayId: Int = DEFAULT_DISPLAY,
+ deskId: Int,
+ displayId: Int,
unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
): IBinder {
+ logV(
+ "startLaunchTransition type=%s launchingTaskId=%d deskId=%d displayId=%d",
+ WindowManager.transitTypeToString(transitionType),
+ launchingTaskId,
+ deskId,
+ displayId,
+ )
// TODO: b/397619806 - Consolidate sharable logic with [handleFreeformTaskLaunch].
var launchTransaction = wct
val taskIdToMinimize =
addAndGetMinimizeChanges(
- displayId,
+ deskId,
launchTransaction,
newTaskId = launchingTaskId,
launchingNewIntent = launchingTaskId == null,
@@ -1027,21 +1046,20 @@ class DesktopTasksController(
)
var activationRunOnTransitStart: RunOnTransitStart? = null
val shouldActivateDesk =
- (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue ||
- DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) &&
- !isDesktopModeShowing(displayId)
+ when {
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ->
+ !taskRepository.isDeskActive(deskId)
+ DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue -> {
+ !isDesktopModeShowing(displayId)
+ }
+ else -> false
+ }
if (shouldActivateDesk) {
- val deskIdToActivate =
- checkNotNull(
- launchingTaskId?.let { taskRepository.getDeskIdForTask(it) }
- ?: getDefaultDeskId(displayId)
- )
val activateDeskWct = WindowContainerTransaction()
// TODO: b/391485148 - pass in the launching task here to apply task-limit policy,
// but make sure to not do it twice since it is also done at the start of this
// function.
- activationRunOnTransitStart =
- addDeskActivationChanges(deskIdToActivate, activateDeskWct)
+ activationRunOnTransitStart = addDeskActivationChanges(deskId, activateDeskWct)
// Desk activation must be handled before app launch-related transactions.
activateDeskWct.merge(launchTransaction, /* transfer= */ true)
launchTransaction = activateDeskWct
@@ -1152,7 +1170,14 @@ class DesktopTasksController(
}
wct.sendPendingIntent(pendingIntent, intent, ops.toBundle())
- startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+ val deskId = getDefaultDeskId(displayId)
+ startLaunchTransition(
+ TRANSIT_OPEN,
+ wct,
+ launchingTaskId = null,
+ deskId = deskId,
+ displayId = displayId,
+ )
}
/**
@@ -2141,10 +2166,14 @@ class DesktopTasksController(
WINDOWING_MODE_FREEFORM -> {
val wct = WindowContainerTransaction()
wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
+ val deskId =
+ taskRepository.getDeskIdForTask(callingTaskInfo.taskId)
+ ?: getDefaultDeskId(callingTaskInfo.displayId)
startLaunchTransition(
transitionType = TRANSIT_OPEN,
wct = wct,
launchingTaskId = null,
+ deskId = deskId,
displayId = callingTaskInfo.displayId,
)
}
@@ -2224,6 +2253,7 @@ class DesktopTasksController(
logV("skip keyguard is locked")
return null
}
+ val deskId = getDefaultDeskId(task.displayId)
val wct = WindowContainerTransaction()
if (shouldFreeformTaskLaunchSwitchToFullscreen(task)) {
logD("Bring desktop tasks to front on transition=taskId=%d", task.taskId)
@@ -2246,7 +2276,6 @@ class DesktopTasksController(
runOnTransitStart?.invoke(transition)
return wct
}
- val deskId = getDefaultDeskId(task.displayId)
val runOnTransitStart = addDeskActivationChanges(deskId, wct, task)
runOnTransitStart?.invoke(transition)
wct.reorder(task.token, true)
@@ -2288,7 +2317,7 @@ class DesktopTasksController(
reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
// 2) minimize a Task if needed.
- val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ val taskIdToMinimize = addAndGetMinimizeChanges(deskId, wct, task.taskId)
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
if (taskIdToMinimize != null) {
addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
@@ -2334,7 +2363,7 @@ class DesktopTasksController(
// The desk was already showing and we're launching a new Task - we
// might need to minimize another Task.
val taskIdToMinimize =
- addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ addAndGetMinimizeChanges(deskId, wct, task.taskId)
taskIdToMinimize?.let { minimizingTaskId ->
addPendingMinimizeTransition(
transition,
@@ -2670,7 +2699,7 @@ class DesktopTasksController(
/** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
private fun addAndGetMinimizeChanges(
- displayId: Int,
+ deskId: Int,
wct: WindowContainerTransaction,
newTaskId: Int?,
launchingNewIntent: Boolean = false,
@@ -2679,7 +2708,7 @@ class DesktopTasksController(
require(newTaskId == null || !launchingNewIntent)
return desktopTasksLimiter
.get()
- .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
+ .addAndGetMinimizeTaskChanges(deskId, wct, newTaskId, launchingNewIntent)
}
private fun addPendingMinimizeTransition(
@@ -2782,9 +2811,20 @@ class DesktopTasksController(
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(displayId)
)
- // TODO: b/362720497 - activating a desk with the intention to move a new task to
- // it means we may need to minimize something in the activating desk. Do so here
- // similar to how it's done in #bringDesktopAppsToFront.
+ val expandedTasksOrderedFrontToBack =
+ taskRepository.getExpandedTasksIdsInDeskOrdered(deskId = deskId)
+ // If we're adding a new Task we might need to minimize an old one
+ val taskIdToMinimize =
+ desktopTasksLimiter
+ .getOrNull()
+ ?.getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
+ if (taskIdToMinimize != null) {
+ val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
+ // TODO(b/365725441): Handle non running task minimization
+ if (taskToMinimize != null) {
+ desksOrganizer.minimizeTask(wct, deskId, taskToMinimize)
+ }
+ }
return { transition ->
val activateDeskTransition =
if (newTaskIdInFront != null) {
@@ -2802,6 +2842,9 @@ class DesktopTasksController(
)
}
desksTransitionObserver.addPendingTransition(activateDeskTransition)
+ taskIdToMinimize?.let { minimizingTask ->
+ addPendingMinimizeTransition(transition, minimizingTask, MinimizeReason.TASK_LIMIT)
+ }
}
}
@@ -3381,7 +3424,14 @@ class DesktopTasksController(
if (windowingMode == WINDOWING_MODE_FREEFORM) {
if (DesktopModeFlags.ENABLE_DESKTOP_TAB_TEARING_MINIMIZE_ANIMATION_BUGFIX.isTrue()) {
// TODO b/376389593: Use a custom tab tearing transition/animation
- startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+ val deskId = getDefaultDeskId(DEFAULT_DISPLAY)
+ startLaunchTransition(
+ TRANSIT_OPEN,
+ wct,
+ launchingTaskId = null,
+ deskId = deskId,
+ displayId = DEFAULT_DISPLAY,
+ )
} else {
desktopModeDragAndDropTransitionHandler.handleDropEvent(wct)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index da369f094405..4ca58823b52b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -22,6 +22,7 @@ import android.os.Handler
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
@@ -31,6 +32,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.UserChangeListener
@@ -48,6 +50,7 @@ class DesktopTasksLimiter(
transitions: Transitions,
private val desktopUserRepositories: DesktopUserRepositories,
private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val desksOrganizer: DesksOrganizer,
private val maxTasksLimit: Int?,
private val interactionJankMonitor: InteractionJankMonitor,
private val context: Context,
@@ -258,7 +261,7 @@ class DesktopTasksLimiter(
* returning the task to minimize.
*/
fun addAndGetMinimizeTaskChanges(
- displayId: Int,
+ deskId: Int,
wct: WindowContainerTransaction,
newFrontTaskId: Int?,
launchingNewIntent: Boolean = false,
@@ -267,15 +270,19 @@ class DesktopTasksLimiter(
val taskRepository = desktopUserRepositories.current
val taskIdToMinimize =
getTaskIdToMinimize(
- taskRepository.getExpandedTasksOrdered(displayId),
+ taskRepository.getExpandedTasksIdsInDeskOrdered(deskId),
newFrontTaskId,
launchingNewIntent,
)
- // If it's a running task, reorder it to back.
taskIdToMinimize
?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
- // TODO: b/391485148 - this won't really work with multi-desks enabled.
- ?.let { wct.reorder(it.token, /* onTop= */ false) }
+ ?.let { task ->
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ wct.reorder(task.token, /* onTop= */ false)
+ } else {
+ desksOrganizer.minimizeTask(wct, deskId, task)
+ }
+ }
return taskIdToMinimize
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index bd9c30e2a495..7dabeb7c9d15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -36,7 +36,6 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
-import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -58,7 +57,6 @@ class DesktopTasksTransitionObserver(
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
private val backAnimationController: BackAnimationController,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
- private val desksTransitionObserver: DesksTransitionObserver,
shellInit: ShellInit,
) : Transitions.TransitionObserver {
@@ -88,7 +86,6 @@ class DesktopTasksTransitionObserver(
finishTransaction: SurfaceControl.Transaction,
) {
// TODO: b/332682201 Update repository state
- desksTransitionObserver.onTransitionReady(transition, info)
if (
DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
.isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index e943c42dcdfc..4f511a901756 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -327,8 +327,9 @@ sealed class DragToDesktopTransitionHandler(
val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo")
val dragPosition = PointF(state.dragAnimator.position)
val scale = state.dragAnimator.scale
+ val cornerRadius = state.dragAnimator.cornerRadius
state.dragAnimator.cancelAnimator()
- requestBubble(wct, taskInfo, onLeft, scale, dragPosition)
+ requestBubble(wct, taskInfo, onLeft, scale, cornerRadius, dragPosition)
}
private fun requestBubble(
@@ -336,13 +337,14 @@ sealed class DragToDesktopTransitionHandler(
taskInfo: RunningTaskInfo,
onLeft: Boolean,
taskScale: Float = 1f,
+ cornerRadius: Float = 0f,
dragPosition: PointF = PointF(0f, 0f),
) {
val controller =
bubbleController.orElseThrow { IllegalStateException("BubbleController not set") }
controller.expandStackAndSelectBubble(
taskInfo,
- BubbleTransitions.DragData(onLeft, taskScale, dragPosition, wct),
+ BubbleTransitions.DragData(onLeft, taskScale, cornerRadius, dragPosition, wct),
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index fc359d7d67b6..8c4bc2598dff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -40,6 +40,13 @@ interface DesksOrganizer {
task: ActivityManager.RunningTaskInfo,
)
+ /** Reorders a desk's task to the front. */
+ fun reorderTaskToFront(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ )
+
/** Minimizes the given task of the given deskId. */
fun minimizeTask(
wct: WindowContainerTransaction,
@@ -47,6 +54,13 @@ interface DesksOrganizer {
task: ActivityManager.RunningTaskInfo,
)
+ /** Unminimize the given task of the given desk. */
+ fun unminimizeTask(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ )
+
/** Whether the change is for the given desk id. */
fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index f576258ebdaa..2130474ff603 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -110,7 +110,31 @@ class RootTaskDesksOrganizer(
wct.reparent(task.token, root.taskInfo.token, /* onTop= */ true)
}
+ override fun reorderTaskToFront(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: RunningTaskInfo,
+ ) {
+ logV("reorderTaskToFront task=${task.taskId} desk=$deskId")
+ val root = deskRootsByDeskId[deskId] ?: error("Root not found for desk: $deskId")
+ if (task.taskId in root.children) {
+ wct.reorder(task.token, /* onTop= */ true, /* includingParents= */ true)
+ return
+ }
+ val minimizationRoot =
+ checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+ "Minimization root not found for desk: $deskId"
+ }
+ if (task.taskId in minimizationRoot.children) {
+ unminimizeTask(wct, deskId, task)
+ wct.reorder(task.token, /* onTop= */ true, /* includingParents= */ true)
+ return
+ }
+ logE("Attempted to reorder task=${task.taskId} in desk=$deskId but it was not a child")
+ }
+
override fun minimizeTask(wct: WindowContainerTransaction, deskId: Int, task: RunningTaskInfo) {
+ logV("minimizeTask task=${task.taskId} desk=$deskId")
val deskRoot =
checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
val minimizationRoot =
@@ -129,6 +153,30 @@ class RootTaskDesksOrganizer(
wct.reparent(task.token, minimizationRoot.token, /* onTop= */ true)
}
+ override fun unminimizeTask(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: RunningTaskInfo,
+ ) {
+ val taskId = task.taskId
+ logV("unminimizeTask task=$taskId desk=$deskId")
+ val deskRoot =
+ checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ val minimizationRoot =
+ checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+ "Minimization root not found for desk: $deskId"
+ }
+ if (taskId in deskRoot.children) {
+ logV("Task #$taskId is already unminimized in desk=$deskId")
+ return
+ }
+ if (taskId !in minimizationRoot.children) {
+ logE("Attempted to unminimize task=$taskId in desk=$deskId but it was not a child")
+ return
+ }
+ wct.reparent(task.token, deskRoot.token, /* onTop= */ true)
+ }
+
override fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean =
(isDeskRootChange(change) && change.taskId == deskId) ||
(getDeskMinimizationRootInChange(change)?.deskId == deskId)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 8059b94685ba..f89ba0a168d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
@@ -52,6 +53,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
private final WindowDecorViewModel mWindowDecorViewModel;
private final Optional<TaskChangeListener> mTaskChangeListener;
private final FocusTransitionObserver mFocusTransitionObserver;
+ private final Optional<DesksTransitionObserver> mDesksTransitionObserver;
private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo =
new HashMap<>();
@@ -63,12 +65,14 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
Optional<DesktopImmersiveController> desktopImmersiveController,
WindowDecorViewModel windowDecorViewModel,
Optional<TaskChangeListener> taskChangeListener,
- FocusTransitionObserver focusTransitionObserver) {
+ FocusTransitionObserver focusTransitionObserver,
+ Optional<DesksTransitionObserver> desksTransitionObserver) {
mTransitions = transitions;
mDesktopImmersiveController = desktopImmersiveController;
mWindowDecorViewModel = windowDecorViewModel;
mTaskChangeListener = taskChangeListener;
mFocusTransitionObserver = focusTransitionObserver;
+ mDesksTransitionObserver = desksTransitionObserver;
if (FreeformComponents.requiresFreeformComponents(context)) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -85,6 +89,10 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT) {
+ // Update desk state first, otherwise [TaskChangeListener] may update desktop task state
+ // under an outdated active desk if a desk switch and a task update happen in the same
+ // transition, such as when unminimizing a task from an inactive desk.
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionReady(transition, info));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository
// is updated from there **before** the |mWindowDecorViewModel| methods are invoked.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 65099c2dfb9d..671eae3d84ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -153,7 +153,12 @@ public class PhonePipMenuController implements PipMenuController,
mPipUiEventLogger = pipUiEventLogger;
mPipTransitionState.addPipTransitionStateChangedListener(this);
-
+ // Clear actions after exit PiP. Otherwise, next PiP could accidentally inherit the
+ // actions provided by the previous app in PiP mode.
+ mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) -> {
+ if (mAppActions != null) mAppActions.clear();
+ mCloseAction = null;
+ }));
mPipTaskListener.addParamsChangedListener(new PipTaskListener.PipParamsChangedCallback() {
@Override
public void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index d6634845ee21..294ef48c01d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -61,7 +61,7 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
private final PipBoundsState mPipBoundsState;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final ShellExecutor mMainExecutor;
- private final PictureInPictureParams mPictureInPictureParams =
+ private PictureInPictureParams mPictureInPictureParams =
new PictureInPictureParams.Builder().build();
private boolean mWaitingForAspectRatioChange = false;
@@ -92,6 +92,11 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
}
mPipResizeAnimatorSupplier = PipResizeAnimator::new;
mPipScheduler.setPipParamsSupplier(this::getPictureInPictureParams);
+ // Reset {@link #mPictureInPictureParams} after exiting PiP. For instance, next Activity
+ // with null aspect ratio would accidentally inherit the aspect ratio from a previous
+ // PiP Activity.
+ mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) ->
+ mPictureInPictureParams = new PictureInPictureParams.Builder().build()));
}
void setPictureInPictureParams(@Nullable PictureInPictureParams params) {
@@ -138,9 +143,8 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
if (mPictureInPictureParams.hasSetAspectRatio()
&& mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(newAspectRatio)
&& PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
- mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
- onAspectRatioChanged(newAspectRatio);
- });
+ mPipTransitionState.setOnIdlePipTransitionStateRunnable(
+ () -> onAspectRatioChanged(newAspectRatio));
}
}
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 7472b0ea56ca..c370c0cb0930 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
@@ -2920,7 +2920,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering, SNAP_TO_2_50_50);
- } else if (isSplitScreenVisible() && isOpening) {
+ } else if (enableFlexibleTwoAppSplit() && isSplitScreenVisible() && isOpening) {
// launching into an existing split stage; possibly launchAdjacent
// If we're replacing a pip-able app, we need to let mixed handler take care of
// it. Otherwise we'll just treat it as an enter+resize
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index 22bc9782170b..cf536eba8382 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -42,8 +42,6 @@ class MoveToDesktopAnimator @JvmOverloads constructor(
.setDuration(ANIMATION_DURATION.toLong())
.apply {
val t = SurfaceControl.Transaction()
- val cornerRadius = context.resources
- .getDimensionPixelSize(R.dimen.desktop_mode_dragged_task_radius).toFloat()
addUpdateListener {
setTaskPosition(mostRecentInput.x, mostRecentInput.y)
t.setScale(taskSurface, scale, scale)
@@ -57,6 +55,8 @@ class MoveToDesktopAnimator @JvmOverloads constructor(
val taskId get() = taskInfo.taskId
val position: PointF = PointF(0.0f, 0.0f)
+ val cornerRadius: Float = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_dragged_task_radius).toFloat()
/**
* Whether motion events from the drag gesture should affect the dragged surface or not. Used
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index c3f5b3f20f4a..30712b55bdfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -466,11 +466,7 @@ class AppHeaderViewHolder(
override fun onHandleMenuOpened() {}
- override fun onHandleMenuClosed() {
- openMenuButton.post {
- openMenuButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
- }
- }
+ override fun onHandleMenuClosed() {}
fun onMaximizeWindowHoverExit() {
maximizeButtonView.cancelHoverAnimation()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 3c79ea7be39f..925ca0f1638d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -221,8 +221,8 @@ public class BubbleTransitionsTest extends ShellTestCase {
PointF dragPosition = new PointF(10f, 20f);
BubbleTransitions.DragData dragData = new BubbleTransitions.DragData(
- /* releasedOnLeft= */ false, /* taskScale= */ 0.5f, dragPosition,
- pendingWct);
+ /* releasedOnLeft= */ false, /* taskScale= */ 0.5f, /* cornerRadius= */ 10f,
+ dragPosition, pendingWct);
final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
@@ -253,6 +253,8 @@ public class BubbleTransitionsTest extends ShellTestCase {
verify(startT).setPosition(snapshot, dragPosition.x, dragPosition.y);
// Snapshot has the scale of the dragged task
verify(startT).setScale(snapshot, dragData.getTaskScale(), dragData.getTaskScale());
+ // Snapshot has dragged task corner radius
+ verify(startT).setCornerRadius(snapshot, dragData.getCornerRadius());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java
index 01b76edd9b25..1066276becc7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java
@@ -19,6 +19,8 @@ package com.android.wm.shell.common.pip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -128,6 +130,31 @@ public class PipBoundsStateTest extends ShellTestCase {
}
@Test
+ public void setLastPipComponentName_notChanged_doesNotCallbackComponentChangedListener() {
+ mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+ PipBoundsState.OnPipComponentChangedListener mockListener =
+ mock(PipBoundsState.OnPipComponentChangedListener.class);
+
+ mPipBoundsState.addOnPipComponentChangedListener(mockListener);
+ mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+
+ verify(mockListener, never()).onPipComponentChanged(any(), any());
+ }
+
+ @Test
+ public void setLastPipComponentName_changed_callbackComponentChangedListener() {
+ mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+ PipBoundsState.OnPipComponentChangedListener mockListener =
+ mock(PipBoundsState.OnPipComponentChangedListener.class);
+
+ mPipBoundsState.addOnPipComponentChangedListener(mockListener);
+ mPipBoundsState.setLastPipComponentName(mTestComponentName2);
+
+ verify(mockListener).onPipComponentChanged(
+ eq(mTestComponentName1), eq(mTestComponentName2));
+ }
+
+ @Test
public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 8f499c959759..87b7d344a3ec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -323,6 +323,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
transitions,
userRepositories,
shellTaskOrganizer,
+ desksOrganizer,
MAX_TASK_LIMIT,
mockInteractionJankMonitor,
mContext,
@@ -2363,6 +2364,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun moveTaskToFront_postsWctWithReorderOp() {
val task1 = setUpFreeformTask()
setUpFreeformTask()
@@ -2385,9 +2387,34 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_reordersToFront() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(task1.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(task1, remoteTransition = null)
+
+ verify(desksOrganizer).reorderTaskToFront(any(), eq(0), eq(task1))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_bringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
setUpHomeTask()
- val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ }
whenever(
desktopMixedTransitionHandler.startLaunchTransition(
eq(TRANSIT_TO_FRONT),
@@ -2408,6 +2435,32 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_bringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
+ val deskId = 0
+ setUpHomeTask()
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(freeformTasks[0].taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[1])
+ }
+
+ @Test
fun moveTaskToFront_minimizedTask_marksTaskAsUnminimized() {
val transition = Binder()
val freeformTask = setUpFreeformTask()
@@ -2496,8 +2549,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
val task = createRecentTaskInfo(1001)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
whenever(
@@ -2520,6 +2578,33 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ val task = createRecentTaskInfo(1001)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ eq(task.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+ }
+
+ @Test
fun moveToNextDisplay_noOtherDisplays() {
whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -3530,7 +3615,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksDisabled_dontMinimize() {
val freeformTask = setUpFreeformTask()
markTaskVisible(freeformTask)
val fullscreenTask = createFullscreenTask()
@@ -3543,7 +3629,29 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksEnabled_dontMinimize() {
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val freeformTask =
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
+ markTaskVisible(it)
+ }
+
+ // Launch a fullscreen task while in the desk.
+ val fullscreenTask = createFullscreenTask()
+ val transition = Binder()
+ val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+ assertNotNull(wct)
+ verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(deskId), any())
+ assertNull(desktopTasksLimiter.getMinimizingTask(transition))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
freeformTasks.forEach { markTaskVisible(it) }
val fullscreenTask = createFullscreenTask()
@@ -3557,7 +3665,34 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
+ markTaskVisible(it)
+ }
+ }
+
+ // Launch a fullscreen task while in the desk.
+ setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+ val transition = Binder()
+ val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+ assertNotNull(wct)
+ verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+ val minimizingTask =
+ assertNotNull(desktopTasksLimiter.getMinimizingTask(transition)?.taskId)
+ assertThat(minimizingTask).isEqualTo(freeformTasks[0].taskId)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
freeformTasks.forEach { markTaskVisible(it) }
val fullscreenTask = createFullscreenTask()
@@ -3578,7 +3713,31 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct)
+ // The launching task is moved to the desk.
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+ // The bottom-most task is minimized.
+ verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksDisabled_existingAndNewTasksAreMinimized() {
val minimizedTask = setUpFreeformTask()
taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
@@ -3604,6 +3763,88 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksEnabled_existingAndNewTasksAreMinimized() {
+ // A desk with a minimized tasks, and non-minimized tasks already at the task limit.
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val minimizedTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.minimizeTaskInDesk(
+ displayId = DEFAULT_DISPLAY,
+ deskId = deskId,
+ taskId = minimizedTask.taskId,
+ )
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
+ markTaskVisible(it)
+ }
+ }
+
+ // Launch a fullscreen task that brings Home to front with it.
+ setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct)
+ verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_taskAddedToDesk() {
+ // A desk with a minimized tasks, and non-minimized tasks already at the task limit.
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+
+ // Launch a fullscreen task that brings Home to front with it.
+ setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct)
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun handleRequest_fullscreenTaskWithTaskOnHome_activatesDesk() {
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+
+ // Launch a fullscreen task that brings Home to front with it.
+ val homeTask = setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+ val transition = Binder()
+ val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+ assertNotNull(wct)
+ wct.assertReorder(homeTask, toTop = true)
+ wct.assertReorder(wallpaperToken, toTop = true)
+ verify(desksOrganizer).activateDesk(wct, deskId)
+ verify(desksTransitionsObserver)
+ .addPendingTransition(
+ DeskTransition.ActiveDeskWithTask(
+ token = transition,
+ displayId = DEFAULT_DISPLAY,
+ deskId = deskId,
+ enterTaskId = fullscreenTask.taskId,
+ )
+ )
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
@@ -3663,8 +3904,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
- val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksDisabled_minimize() {
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
freeformTasks.forEach { markTaskVisible(it) }
val newFreeformTask = createFreeformTask()
@@ -3677,6 +3923,24 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksEnabled_minimize() {
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val newFreeformTask = createFreeformTask()
+
+ val wct =
+ controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
+
+ assertNotNull(wct)
+ verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() {
val deskId = 0
val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
@@ -5578,7 +5842,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromFreeformAddsNewWindow() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun openInstance_fromFreeformAddsNewWindow_multiDesksDisabled() {
setUpLandscapeDisplay()
val task = setUpFreeformTask()
val taskToRequest = setUpFreeformTask()
@@ -5592,7 +5857,9 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
)
)
.thenReturn(Binder())
+
runOpenInstance(task, taskToRequest.taskId)
+
verify(desktopMixedTransitionHandler)
.startLaunchTransition(anyInt(), any(), anyInt(), anyOrNull(), anyOrNull())
val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
@@ -5601,10 +5868,42 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun openInstance_fromFreeformAddsNewWindow_multiDesksEnabled() {
+ setUpLandscapeDisplay()
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ val taskToRequest = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_TO_FRONT),
+ any(),
+ eq(taskToRequest.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ runOpenInstance(task, taskToRequest.taskId)
+
+ verify(desktopMixedTransitionHandler)
+ .startLaunchTransition(anyInt(), any(), anyInt(), anyOrNull(), anyOrNull())
+ verify(desksOrganizer).reorderTaskToFront(any(), eq(0), eq(taskToRequest))
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
- fun openInstance_fromFreeform_minimizesIfNeeded() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun openInstance_fromFreeform_multiDesksDisabled_minimizesIfNeeded() {
setUpLandscapeDisplay()
- val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
val oldestTask = freeformTasks.first()
val newestTask = freeformTasks.last()
@@ -5630,6 +5929,40 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun openInstance_fromFreeform_multiDesksEnabled_minimizesIfNeeded() {
+ setUpLandscapeDisplay()
+ val deskId = 0
+ val freeformTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ }
+ val oldestTask = freeformTasks.first()
+ val newestTask = freeformTasks.last()
+
+ val transition = Binder()
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ anyInt(),
+ wctCaptor.capture(),
+ anyInt(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+ val wct = wctCaptor.firstValue
+ verify(desksOrganizer).minimizeTask(wct, deskId, oldestTask)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
setUpLandscapeDisplay()
@@ -6587,7 +6920,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
fun startLaunchTransition_desktopNotShowing_movesWallpaperToFront() {
- val launchingTask = createFreeformTask()
+ val launchingTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
val wct = WindowContainerTransaction()
wct.reorder(launchingTask.token, /* onTop= */ true)
whenever(
@@ -6601,7 +6934,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
)
.thenReturn(Binder())
- controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+ controller.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ launchingTaskId = null,
+ deskId = 0,
+ displayId = DEFAULT_DISPLAY,
+ )
val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
val launchingTaskReorderIndex = latestWct.indexOfReorder(launchingTask, toTop = true)
@@ -6625,6 +6964,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
transitionType = TRANSIT_OPEN,
wct = WindowContainerTransaction(),
launchingTaskId = null,
+ deskId = 0,
+ displayId = DEFAULT_DISPLAY,
)
verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(any())
@@ -6634,6 +6975,51 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun startLaunchTransition_launchingTaskFromInactiveDesk_otherDeskActive_activatesDesk() {
+ val activeDeskId = 4
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = activeDeskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = activeDeskId)
+ val inactiveDesk = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = inactiveDesk)
+ val launchingTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = inactiveDesk)
+ val transition = Binder()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ eq(launchingTask.taskId),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(transition)
+
+ val wct = WindowContainerTransaction()
+ controller.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ launchingTaskId = launchingTask.taskId,
+ deskId = inactiveDesk,
+ displayId = DEFAULT_DISPLAY,
+ )
+
+ verify(desksOrganizer).activateDesk(any(), eq(inactiveDesk))
+ verify(desksTransitionsObserver)
+ .addPendingTransition(
+ DeskTransition.ActivateDesk(
+ token = transition,
+ displayId = DEFAULT_DISPLAY,
+ deskId = inactiveDesk,
+ )
+ )
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
fun startLaunchTransition_desktopShowing_doesNotReorderWallpaper() {
val wct = WindowContainerTransaction()
@@ -6648,8 +7034,14 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
)
.thenReturn(Binder())
- setUpFreeformTask()
- controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+ setUpFreeformTask(deskId = 0, displayId = DEFAULT_DISPLAY)
+ controller.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ launchingTaskId = null,
+ deskId = 0,
+ displayId = DEFAULT_DISPLAY,
+ )
val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 76103640c029..eeecb00b5b08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -39,12 +39,14 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -67,10 +69,13 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
import org.mockito.quality.Strictness
/**
@@ -84,6 +89,7 @@ import org.mockito.quality.Strictness
class DesktopTasksLimiterTest : ShellTestCase() {
@Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock lateinit var desksOrganizer: DesksOrganizer
@Mock lateinit var transitions: Transitions
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock lateinit var handler: Handler
@@ -128,6 +134,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
transitions,
userRepositories,
shellTaskOrganizer,
+ desksOrganizer,
MAX_TASK_LIMIT,
interactionJankMonitor,
mContext,
@@ -148,6 +155,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
transitions,
userRepositories,
shellTaskOrganizer,
+ desksOrganizer,
0,
interactionJankMonitor,
mContext,
@@ -163,6 +171,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
transitions,
userRepositories,
shellTaskOrganizer,
+ desksOrganizer,
-5,
interactionJankMonitor,
mContext,
@@ -178,6 +187,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
transitions,
userRepositories,
shellTaskOrganizer,
+ desksOrganizer,
maxTasksLimit = null,
interactionJankMonitor,
mContext,
@@ -394,7 +404,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
- fun addAndGetMinimizeTaskChanges_tasksWithinLimit_noTaskMinimized() {
+ @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addAndGetMinimizeTaskChanges_tasksWithinLimit_multiDesksDisabled_noTaskMinimized() {
desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
@@ -402,7 +413,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
val minimizedTaskId =
desktopTasksLimiter.addAndGetMinimizeTaskChanges(
- displayId = DEFAULT_DISPLAY,
+ deskId = 0,
wct = wct,
newFrontTaskId = setUpFreeformTask().taskId,
)
@@ -412,7 +423,27 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
- fun addAndGetMinimizeTaskChanges_tasksAboveLimit_backTaskMinimized() {
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addAndGetMinimizeTaskChanges_tasksWithinLimit_multiDesksEnabled_noTaskMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
+
+ val wct = WindowContainerTransaction()
+ val minimizedTaskId =
+ desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+ deskId = 0,
+ wct = wct,
+ newFrontTaskId = setUpFreeformTask().taskId,
+ )
+
+ assertThat(minimizedTaskId).isNull()
+ verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(0), any())
+ }
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addAndGetMinimizeTaskChanges_tasksAboveLimit_multiDesksDisabled_backTaskMinimized() {
desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
// The following list will be ordered bottom -> top, as the last task is moved to top last.
@@ -421,7 +452,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
val minimizedTaskId =
desktopTasksLimiter.addAndGetMinimizeTaskChanges(
- displayId = DEFAULT_DISPLAY,
+ deskId = DEFAULT_DISPLAY,
wct = wct,
newFrontTaskId = setUpFreeformTask().taskId,
)
@@ -433,7 +464,28 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
- fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_noTaskMinimized() {
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addAndGetMinimizeTaskChanges_tasksAboveLimit_multiDesksEnabled_backTaskMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ // The following list will be ordered bottom -> top, as the last task is moved to top last.
+ val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+
+ val wct = WindowContainerTransaction()
+ val minimizedTaskId =
+ desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+ deskId = DEFAULT_DISPLAY,
+ wct = wct,
+ newFrontTaskId = setUpFreeformTask().taskId,
+ )
+
+ assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
+ verify(desksOrganizer).minimizeTask(wct, deskId = 0, tasks.first())
+ }
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_multiDesksDisabled_noTaskMinimized() {
desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
@@ -442,7 +494,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val wct = WindowContainerTransaction()
val minimizedTaskId =
desktopTasksLimiter.addAndGetMinimizeTaskChanges(
- displayId = 0,
+ deskId = 0,
wct = wct,
newFrontTaskId = setUpFreeformTask().taskId,
)
@@ -452,6 +504,26 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_multiDesksEnabled_noTaskMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+ desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId)
+
+ val wct = WindowContainerTransaction()
+ val minimizedTaskId =
+ desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+ deskId = 0,
+ wct = wct,
+ newFrontTaskId = setUpFreeformTask().taskId,
+ )
+
+ assertThat(minimizedTaskId).isNull()
+ verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(0), any())
+ }
+
+ @Test
fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
@@ -485,6 +557,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
transitions,
userRepositories,
shellTaskOrganizer,
+ desksOrganizer,
MAX_TASK_LIMIT2,
interactionJankMonitor,
mContext,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index fd8842b6d99b..a7dc706eb6c9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -48,13 +48,11 @@ import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
-import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP
import com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP
-import com.android.wm.shell.util.StubTransaction
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
@@ -95,7 +93,6 @@ class DesktopTasksTransitionObserverTest {
private val backAnimationController = mock<BackAnimationController>()
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
- private val desksTransitionObserver = mock<DesksTransitionObserver>()
private val wallpaperToken = MockToken().token()
private lateinit var transitionObserver: DesktopTasksTransitionObserver
@@ -119,7 +116,6 @@ class DesktopTasksTransitionObserverTest {
mixedHandler,
backAnimationController,
desktopWallpaperActivityTokenProvider,
- desksTransitionObserver,
shellInit,
)
}
@@ -403,21 +399,6 @@ class DesktopTasksTransitionObserverTest {
verify(taskRepository).setTaskInPip(task.displayId, task.taskId, enterPip = false)
}
- @Test
- fun onTransitionReady_forwardsToDesksTransitionObserver() {
- val transition = Binder()
- val info = TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0)
-
- transitionObserver.onTransitionReady(
- transition = transition,
- info = info,
- StubTransaction(),
- StubTransaction(),
- )
-
- verify(desksTransitionObserver).onTransitionReady(transition, info)
- }
-
private fun createBackNavigationTransition(
task: RunningTaskInfo?,
type: Int = TRANSIT_TO_BACK,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 96b85ad2729e..891a1f1685cd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -21,9 +21,11 @@ import android.view.Display
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
+import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.Change
import android.window.WindowContainerTransaction.HierarchyOp
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTaskOrganizer
@@ -473,14 +475,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
- assertThat(
- wct.hierarchyOps.any { hop ->
- hop.isReparent &&
- hop.container == task.token.asBinder() &&
- hop.newParent == desk.minimizationRoot.token.asBinder()
- }
- )
- .isTrue()
+ assertThat(wct.hasMinimizationHops(desk, task.token)).isTrue()
}
@Test
@@ -508,6 +503,110 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
assertThat(wct.isEmpty).isTrue()
}
+ @Test
+ fun unminimizeTask() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+ organizer.onTaskAppeared(task, SurfaceControl())
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+ task.parentTaskId = desk.minimizationRoot.rootId
+ organizer.onTaskInfoChanged(task)
+
+ wct.clear()
+ organizer.unminimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.hasUnminimizationHops(desk, task.token)).isTrue()
+ }
+
+ @Test
+ fun unminimizeTask_alreadyUnminimized_noOp() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ wct.clear()
+ organizer.unminimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.hasUnminimizationHops(desk, task.token)).isFalse()
+ }
+
+ @Test
+ fun unminimizeTask_notInDesk_noOp() {
+ val desk = createDesk()
+ val task = createFreeformTask()
+ val wct = WindowContainerTransaction()
+
+ organizer.unminimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.hasUnminimizationHops(desk, task.token)).isFalse()
+ }
+
+ @Test
+ fun reorderTaskToFront() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.reorderTaskToFront(wct, desk.deskRoot.deskId, task)
+
+ assertThat(
+ wct.hierarchyOps.singleOrNull { hop ->
+ hop.container == task.token.asBinder() &&
+ hop.type == HIERARCHY_OP_TYPE_REORDER &&
+ hop.toTop &&
+ hop.includingParents()
+ }
+ )
+ .isNotNull()
+ }
+
+ @Test
+ fun reorderTaskToFront_notInDesk_noOp() {
+ val desk = createDesk()
+ val task = createFreeformTask()
+ val wct = WindowContainerTransaction()
+
+ organizer.reorderTaskToFront(wct, desk.deskRoot.deskId, task)
+
+ assertThat(
+ wct.hierarchyOps.singleOrNull { hop ->
+ hop.container == task.token.asBinder() &&
+ hop.type == HIERARCHY_OP_TYPE_REORDER &&
+ hop.toTop &&
+ hop.includingParents()
+ }
+ )
+ .isNull()
+ }
+
+ @Test
+ fun reorderTaskToFront_minimized_unminimizesAndReorders() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+ task.parentTaskId = desk.minimizationRoot.rootId
+ organizer.onTaskInfoChanged(task)
+
+ organizer.reorderTaskToFront(wct, desk.deskRoot.deskId, task)
+
+ assertThat(wct.hasUnminimizationHops(desk, task.token)).isTrue()
+ assertThat(
+ wct.hierarchyOps.singleOrNull { hop ->
+ hop.container == task.token.asBinder() &&
+ hop.type == HIERARCHY_OP_TYPE_REORDER &&
+ hop.toTop &&
+ hop.includingParents()
+ }
+ )
+ .isNotNull()
+ }
+
private data class DeskRoots(
val deskRoot: DeskRoot,
val minimizationRoot: DeskMinimizationRoot,
@@ -525,6 +624,27 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
)
}
+ private fun WindowContainerTransaction.hasMinimizationHops(
+ desk: DeskRoots,
+ task: WindowContainerToken,
+ ): Boolean =
+ hierarchyOps.any { hop ->
+ hop.isReparent &&
+ hop.container == task.asBinder() &&
+ hop.newParent == desk.minimizationRoot.token.asBinder()
+ }
+
+ private fun WindowContainerTransaction.hasUnminimizationHops(
+ desk: DeskRoots,
+ task: WindowContainerToken,
+ ): Boolean =
+ hierarchyOps.any { hop ->
+ hop.isReparent &&
+ hop.container == task.asBinder() &&
+ hop.newParent == desk.deskRoot.token.asBinder() &&
+ hop.toTop
+ }
+
private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {
var deskId: Int? = null
val created: Boolean
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index bc918450a3cf..714e5f486285 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -44,10 +44,12 @@ import androidx.test.filters.SmallTest;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.StubTransaction;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before;
@@ -68,6 +70,7 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase {
@Mock private WindowDecorViewModel mWindowDecorViewModel;
@Mock private TaskChangeListener mTaskChangeListener;
@Mock private FocusTransitionObserver mFocusTransitionObserver;
+ @Mock private DesksTransitionObserver mDesksTransitionObserver;
private FreeformTaskTransitionObserver mTransitionObserver;
@@ -88,7 +91,8 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase {
Optional.of(mDesktopImmersiveController),
mWindowDecorViewModel,
Optional.of(mTaskChangeListener),
- mFocusTransitionObserver);
+ mFocusTransitionObserver,
+ Optional.of(mDesksTransitionObserver));
final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver));
@@ -357,6 +361,18 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase {
verify(mDesktopImmersiveController).onTransitionFinished(transition, /* aborted= */ false);
}
+ @Test
+ public void onTransitionReady_forwardsToDesksTransitionObserver() {
+ final IBinder transition = mock(IBinder.class);
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, /* flags= */ 0)
+ .build();
+
+ mTransitionObserver.onTransitionReady(transition, info, new StubTransaction(),
+ new StubTransaction());
+
+ verify(mDesksTransitionObserver).onTransitionReady(transition, info);
+ }
+
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 333569a7206e..5029371c3419 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
import static org.mockito.kotlin.VerificationKt.clearInvocations;
@@ -35,7 +36,9 @@ import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -48,8 +51,10 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
import org.junit.Before;
@@ -107,6 +112,16 @@ public class PipTaskListenerTest {
}
@Test
+ public void constructor_addOnPipComponentChangedListener() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+
+ verify(mMockPipBoundsState).addOnPipComponentChangedListener(
+ any(PipBoundsState.OnPipComponentChangedListener.class));
+ }
+
+ @Test
public void setPictureInPictureParams_updatePictureInPictureParams() {
mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
@@ -359,6 +374,26 @@ public class PipTaskListenerTest {
verify(mMockPipResizeAnimator, times(0)).start();
}
+ @Test
+ public void onPipComponentChanged_clearPictureInPictureParams() {
+ when(mMockContext.getResources()).thenReturn(mock(Resources.class));
+ PipBoundsState pipBoundsState = new PipBoundsState(mMockContext,
+ mock(PhoneSizeSpecSource.class), mock(PipDisplayLayoutState.class));
+ pipBoundsState.setLastPipComponentName(new ComponentName("org.test", "test1"));
+
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, pipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ pipBoundsState.setLastPipComponentName(new ComponentName("org.test", "test2"));
+
+ assertTrue(mPipTaskListener.getPictureInPictureParams().empty());
+ }
+
private PictureInPictureParams getPictureInPictureParams(Rational aspectRatio,
String... actions) {
final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 30594dcfa939..0d45149267cf 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1265,6 +1265,9 @@ struct ResTable_config
// Varies in length from 3 to 8 chars. Zero-filled value.
char localeNumberingSystem[8];
+ // Mark all padding explicitly so it's clear how much we can expand it.
+ char endPadding[3];
+
void copyFromDeviceNoSwap(const ResTable_config& o) {
const auto o_size = dtohl(o.size);
if (o_size >= sizeof(ResTable_config)) [[likely]] {
@@ -1422,6 +1425,13 @@ struct ResTable_config
void swapHtoD_slow();
};
+// Fix the struct size for backward compatibility
+static_assert(sizeof(ResTable_config) == 64);
+
+// Make sure there's no unaccounted padding in the structure.
+static_assert(offsetof(ResTable_config, endPadding) +
+ sizeof(ResTable_config::endPadding) == sizeof(ResTable_config));
+
/**
* A specification of the resources defined by a particular type.
*
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index a210ddf54b2e..7d227f793817 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -722,10 +722,15 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() {
auto canvas = SkCanvas(recycledPixels->getSkBitmap());
SkRect destination = SkRect::Make(recycledPixels->info().bounds());
- destination.intersect(SkRect::Make(mSkiaBitmap->info().bounds()));
- canvas.drawImageRect(mSkiaBitmap->asImage(), *mDesiredSubset, destination,
- SkSamplingOptions(SkFilterMode::kLinear), nullptr,
- SkCanvas::kFast_SrcRectConstraint);
+ if (destination.intersect(SkRect::Make(mSkiaBitmap->info().bounds()))) {
+ canvas.drawImageRect(mSkiaBitmap->asImage(), *mDesiredSubset, destination,
+ SkSamplingOptions(SkFilterMode::kLinear), nullptr,
+ SkCanvas::kFast_SrcRectConstraint);
+ } else {
+ // The canvas would have discarded the draw operation automatically, but
+ // this case should have been detected before getting to this point.
+ ALOGE("Copy destination does not intersect image bounds");
+ }
} else {
void* dst = recycledPixels->pixels();
const size_t dstRowBytes = mRecycledBitmap->rowBytes();
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index dafcc729b8f1..d929b0de391a 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,6 +187,9 @@
<!-- Default state of tap to wake -->
<bool name="def_double_tap_to_wake">true</bool>
+ <!-- Default setting for double tap to sleep (Settings.Secure.DOUBLE_TAP_TO_SLEEP) -->
+ <bool name="def_double_tap_to_sleep">false</bool>
+
<!-- Default for Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT -->
<string name="def_nfc_payment_component"></string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index c0105298899b..f0a0483aa17c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -85,6 +85,7 @@ public class SecureSettings {
Settings.Secure.MOUNT_UMS_PROMPT,
Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
Settings.Secure.DOUBLE_TAP_TO_WAKE,
+ Settings.Secure.DOUBLE_TAP_TO_SLEEP,
Settings.Secure.WAKE_GESTURE_ENABLED,
Settings.Secure.LONG_PRESS_TIMEOUT,
Settings.Secure.KEY_REPEAT_ENABLED,
@@ -293,5 +294,7 @@ public class SecureSettings {
Settings.Secure.FINGERPRINT_APP_ENABLED,
Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED,
Settings.Secure.DUAL_SHADE,
+ Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED,
+ Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 0ffdf53f2036..b5de7e719043 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -131,6 +131,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.MOUNT_UMS_PROMPT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.MOUNT_UMS_NOTIFY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOUBLE_TAP_TO_WAKE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DOUBLE_TAP_TO_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.WAKE_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LONG_PRESS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.KEY_REPEAT_ENABLED, BOOLEAN_VALIDATOR);
@@ -461,5 +462,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.FINGERPRINT_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FINGERPRINT_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.BROWSER_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SEARCH_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4a225bdbd7e5..65ede9d804d0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4080,7 +4080,7 @@ public class SettingsProvider extends ContentProvider {
@VisibleForTesting
final class UpgradeController {
- private static final int SETTINGS_VERSION = 227;
+ private static final int SETTINGS_VERSION = 228;
private final int mUserId;
@@ -6319,6 +6319,23 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 227;
}
+ // Version 227: Add default value for DOUBLE_TAP_TO_SLEEP.
+ if (currentVersion == 227) {
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting doubleTapToSleep = secureSettings.getSettingLocked(
+ Settings.Secure.DOUBLE_TAP_TO_SLEEP);
+ if (doubleTapToSleep.isNull()) {
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.DOUBLE_TAP_TO_SLEEP,
+ getContext().getResources().getBoolean(
+ R.bool.def_double_tap_to_sleep) ? "1" : "0",
+ null /* tag */,
+ true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 228;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
index 96feeedb8793..e734dd26eb15 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
@@ -25,6 +25,7 @@ data class AxisDefinition(
)
object GSFAxes {
+ @JvmStatic
val WEIGHT =
AxisDefinition(
tag = "wght",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 5b073e49192a..4a39cff388a9 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -39,6 +39,7 @@ interface TypefaceVariantCache {
fun getTypefaceForVariant(fvar: String?): Typeface?
companion object {
+ @JvmStatic
fun createVariantTypeface(baseTypeface: Typeface, fVar: String?): Typeface {
if (fVar.isNullOrEmpty()) {
return baseTypeface
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index 0bd51cd9822d..44f2353dcb75 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -111,8 +111,8 @@ private constructor(metadata: FailureMetadata, private val actual: TransitionSta
}
companion object {
- fun transitionStates() = Factory { metadata, actual: TransitionState ->
- TransitionStateSubject(metadata, actual)
+ fun transitionStates() = Factory { metadata, actual: TransitionState? ->
+ TransitionStateSubject(metadata, actual!!)
}
}
}
@@ -181,8 +181,8 @@ private constructor(metadata: FailureMetadata, actual: TransitionState.Transitio
companion object {
fun sceneTransitions() =
- Factory { metadata, actual: TransitionState.Transition.ChangeScene ->
- SceneTransitionSubject(metadata, actual)
+ Factory { metadata, actual: TransitionState.Transition.ChangeScene? ->
+ SceneTransitionSubject(metadata, actual!!)
}
}
}
@@ -202,8 +202,8 @@ private constructor(
companion object {
fun showOrHideOverlayTransitions() =
- Factory { metadata, actual: TransitionState.Transition.ShowOrHideOverlay ->
- ShowOrHideOverlayTransitionSubject(metadata, actual)
+ Factory { metadata, actual: TransitionState.Transition.ShowOrHideOverlay? ->
+ ShowOrHideOverlayTransitionSubject(metadata, actual!!)
}
}
}
@@ -221,8 +221,8 @@ private constructor(metadata: FailureMetadata, actual: TransitionState.Transitio
companion object {
fun replaceOverlayTransitions() =
- Factory { metadata, actual: TransitionState.Transition.ReplaceOverlay ->
- ReplaceOverlayTransitionSubject(metadata, actual)
+ Factory { metadata, actual: TransitionState.Transition.ReplaceOverlay? ->
+ ReplaceOverlayTransitionSubject(metadata, actual!!)
}
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
index ab31038fac8f..368a333fbd78 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
@@ -49,8 +49,8 @@ class DpOffsetSubject(metadata: FailureMetadata, private val actual: DpOffset) :
val DefaultTolerance = Dp(.5f)
fun dpOffsets() =
- Factory<DpOffsetSubject, DpOffset> { metadata, actual ->
- DpOffsetSubject(metadata, actual)
+ Factory { metadata, actual: DpOffset? ->
+ DpOffsetSubject(metadata, actual!!)
}
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index e9e61a718f08..37acbe261f76 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -161,15 +161,7 @@ class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
}
override fun onThemeChanged(theme: ThemeConfig) {
- val color =
- when {
- theme.seedColor != null -> theme.seedColor!!
- theme.isDarkTheme ->
- clockCtx.resources.getColor(android.R.color.system_accent1_100)
- else -> clockCtx.resources.getColor(android.R.color.system_accent2_600)
- }
-
- view.updateColor(color)
+ view.updateColor(theme.getDefaultColor(clockCtx.context))
}
override fun onFontSettingChanged(fontSizePx: Float) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 365567b17ec0..bc4bdf4243cb 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -149,14 +149,7 @@ class DefaultClockController(
override fun onThemeChanged(theme: ThemeConfig) {
this@DefaultClockFaceController.theme = theme
- val color =
- when {
- theme.seedColor != null -> theme.seedColor!!
- theme.isDarkTheme ->
- resources.getColor(android.R.color.system_accent1_100)
- else -> resources.getColor(android.R.color.system_accent2_600)
- }
-
+ val color = theme.getDefaultColor(ctx)
if (currentColor == color) {
return
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 97004ef6f9a9..1d963af3ad22 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -229,15 +229,7 @@ open class SimpleDigitalHandLayerController(
}
override fun onThemeChanged(theme: ThemeConfig) {
- val color =
- when {
- theme.seedColor != null -> theme.seedColor!!
- theme.isDarkTheme ->
- clockCtx.resources.getColor(android.R.color.system_accent1_100)
- else -> clockCtx.resources.getColor(android.R.color.system_accent2_600)
- }
-
- view.updateColor(color)
+ view.updateColor(theme.getDefaultColor(clockCtx.context))
refreshTime()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index fae17a5321ff..0ec2d188833a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -45,6 +45,7 @@ import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.replace
import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.toFVar
import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.shared.clocks.CanvasUtil.translate
import com.android.systemui.shared.clocks.CanvasUtil.use
import com.android.systemui.shared.clocks.ClockContext
@@ -330,7 +331,7 @@ open class SimpleDigitalClockTextView(
textAnimator.setTextStyle(
TextAnimator.Style(
fVar = if (isDozing) aodFontVariation else lsFontVariation,
- color = if (isDozing) AOD_COLOR else lockscreenColor,
+ color = if (isDozing && !ambientAod()) AOD_COLOR else lockscreenColor,
textSize = if (isDozing) aodFontSizePx else lockScreenPaint.textSize,
),
TextAnimator.Animation(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index e0515000b232..454c15667f22 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -56,6 +56,7 @@ import com.android.systemui.statusbar.phone.dozeScrimController
import com.android.systemui.statusbar.phone.screenOffAnimationController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
@@ -105,7 +106,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
@Test
fun nonPowerButtonFPS_vibrateSuccess() =
testScope.runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
runCurrent()
enterDeviceFromFingerprintUnlockLegacy()
@@ -116,7 +117,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
@Test
fun powerButtonFPS_vibrateSuccess() =
testScope.runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
@@ -133,7 +134,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
@Test
fun powerButtonFPS_powerDown_doNotVibrateSuccess() =
testScope.runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
@@ -150,7 +151,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
@Test
fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() =
testScope.runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
@@ -174,14 +175,14 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
}
@Test
- fun nonPowerButtonFPS_coExFaceFailure_vibrateError() =
+ fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
testScope.runTest {
val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
enrollFace()
runCurrent()
faceFailure()
- assertThat(playErrorHaptic).isNotNull()
+ assertThat(playErrorHaptic).isNull()
}
@Test
@@ -211,7 +212,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
runCurrent()
configureDeviceEntryFromBiometricSource(isFpUnlock = true)
@@ -225,7 +226,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
@@ -246,18 +247,19 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
enrollFace()
kosmos.configureKeyguardBypass(isBypassAvailable = true)
underTest = kosmos.deviceEntryHapticsInteractor
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
configureDeviceEntryFromBiometricSource(isFaceUnlock = true)
verifyDeviceEntryFromFaceAuth()
assertThat(playSuccessHaptic).isNotNull()
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@EnableSceneContainer
@Test
- fun playSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() =
+ fun skipSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() =
testScope.runTest {
underTest = kosmos.deviceEntryHapticsInteractor
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFace()
kosmos.configureKeyguardBypass(isBypassAvailable = false)
@@ -265,7 +267,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
configureDeviceEntryFromBiometricSource(isFaceUnlock = true, bypassEnabled = false)
kosmos.fakeDeviceEntryFaceAuthRepository.isAuthenticated.value = true
- assertThat(playSuccessHaptic).isNotNull()
+ assertThat(playSuccessHaptic).isNull()
}
@EnableSceneContainer
@@ -274,7 +276,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
// power button is currently DOWN
kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
@@ -295,7 +297,7 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
testScope.runTest {
kosmos.configureKeyguardBypass(isBypassAvailable = false)
underTest = kosmos.deviceEntryHapticsInteractor
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt
new file mode 100644
index 000000000000..d5d256e5cd97
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.model.Statement
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class LogWtfHandlerRuleTest : SysuiTestCase() {
+
+ val underTest = LogWtfHandlerRule()
+
+ @Test
+ fun passingTestWithoutWtf_shouldPass() {
+ val result = runTestCodeWithRule {
+ Log.e(TAG, "just an error", IndexOutOfBoundsException())
+ }
+ assertThat(result.isSuccess).isTrue()
+ }
+
+ @Test
+ fun passingTestWithWtf_shouldFail() {
+ val result = runTestCodeWithRule {
+ Log.wtf(TAG, "some terrible failure", IllegalStateException())
+ }
+ assertThat(result.isFailure).isTrue()
+ val exception = result.exceptionOrNull()
+ assertThat(exception).isInstanceOf(AssertionError::class.java)
+ assertThat(exception?.cause).isInstanceOf(Log.TerribleFailure::class.java)
+ assertThat(exception?.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+ }
+
+ @Test
+ fun failingTestWithoutWtf_shouldFail() {
+ val result = runTestCodeWithRule {
+ Log.e(TAG, "just an error", IndexOutOfBoundsException())
+ throw NullPointerException("some npe")
+ }
+ assertThat(result.isFailure).isTrue()
+ assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+ }
+
+ @Test
+ fun failingTestWithWtf_shouldFail() {
+ val result = runTestCodeWithRule {
+ Log.wtf(TAG, "some terrible failure", IllegalStateException())
+ throw NullPointerException("some npe")
+ }
+ assertThat(result.isFailure).isTrue()
+ assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+ val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions
+ assertThat(suppressedExceptions).hasSize(1)
+ val suppressed = suppressedExceptions.first()
+ assertThat(suppressed).isInstanceOf(AssertionError::class.java)
+ assertThat(suppressed.cause).isInstanceOf(Log.TerribleFailure::class.java)
+ assertThat(suppressed.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+ }
+
+ @Test
+ fun passingTestWithExemptWtf_shouldPass() {
+ underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+ val result = runTestCodeWithRule {
+ Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+ }
+ assertThat(result.isSuccess).isTrue()
+ }
+
+ @Test
+ fun failingTestWithExemptWtf_shouldFail() {
+ underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+ val result = runTestCodeWithRule {
+ Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+ throw NullPointerException("some npe")
+ }
+ assertThat(result.isFailure).isTrue()
+ assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+ val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions
+ assertThat(suppressedExceptions).isEmpty()
+ }
+
+ @Test
+ fun passingTestWithOneExemptWtfOfTwo_shouldFail() {
+ underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+ val result = runTestCodeWithRule {
+ Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+ Log.wtf(TAG, "some terrible failure", IllegalStateException())
+ }
+ assertThat(result.isFailure).isTrue()
+ val exception = result.exceptionOrNull()
+ assertThat(exception).isInstanceOf(AssertionError::class.java)
+ assertThat(exception?.cause).isInstanceOf(Log.TerribleFailure::class.java)
+ assertThat(exception?.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+ }
+
+ @Test
+ fun failingTestWithOneExemptWtfOfTwo_shouldFail() {
+ underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+ val result = runTestCodeWithRule {
+ Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+ Log.wtf(TAG, "some terrible failure", IllegalStateException())
+ throw NullPointerException("some npe")
+ }
+ assertThat(result.isFailure).isTrue()
+ assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+ val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions
+ assertThat(suppressedExceptions).hasSize(1)
+ val suppressed = suppressedExceptions.first()
+ assertThat(suppressed).isInstanceOf(AssertionError::class.java)
+ assertThat(suppressed.cause).isInstanceOf(Log.TerribleFailure::class.java)
+ assertThat(suppressed.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+ }
+
+ private fun runTestCodeWithRule(testCode: () -> Unit): Result<Unit> {
+ val testCodeStatement =
+ object : Statement() {
+ override fun evaluate() {
+ testCode()
+ }
+ }
+ val wrappedTest = underTest.apply(testCodeStatement, mock())
+ return try {
+ wrappedTest.evaluate()
+ Result.success(Unit)
+ } catch (e: Throwable) {
+ Result.failure(e)
+ }
+ }
+
+ companion object {
+ const val TAG = "LogWtfHandlerRuleTest"
+ const val TAG_EXPECTED = "EXPECTED"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 9adf24f32c0c..1743e056b65c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -863,7 +863,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasUdfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -885,7 +885,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasUdfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -907,7 +907,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasSfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -930,7 +930,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasSfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1033,7 +1033,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasSfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1056,7 +1056,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasSfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1079,7 +1079,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasSfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1102,7 +1102,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playSuccessHaptic by
- collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
setupBiometricAuth(hasSfps = true)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1160,7 +1160,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
@Test
@DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun playsFaceErrorHaptics_nonSfps_coEx() =
+ fun skipsFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1172,15 +1172,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
updateFaceAuthStatus(isSuccess = false)
- assertThat(playErrorHaptic).isNotNull()
- assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
- verify(vibratorHelper).vibrateAuthError(anyString())
+ assertThat(playErrorHaptic).isNull()
+ verify(vibratorHelper, never()).vibrateAuthError(anyString())
verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
}
@Test
@EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun playsMSDLFaceErrorHaptics_nonSfps_coEx() =
+ fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1192,10 +1191,9 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
updateFaceAuthStatus(isSuccess = false)
- assertThat(playErrorHaptic).isNotNull()
- assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
- assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
- assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ assertThat(playErrorHaptic).isNull()
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 3407cd50e76f..4a304071ee97 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -41,7 +41,6 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.flag.junit.FlagsParameterization;
-import android.provider.Settings;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.WindowManager;
@@ -71,7 +70,6 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.settings.FakeSettings;
import com.google.common.util.concurrent.MoreExecutors;
@@ -113,7 +111,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
- private FakeSettings mSecureSettings;
private final Executor mMainExecutor = MoreExecutors.directExecutor();
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -135,9 +132,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
- mSecureSettings = new FakeSettings();
- mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 0);
-
// Preferred refresh rate is equal to the first displayMode's refresh rate
mPreferredRefreshRate = mContext.getDisplay().getSystemSupportedModes()[0].getRefreshRate();
overrideResource(
@@ -171,7 +165,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
() -> mSelectedUserInteractor,
mUserTracker,
mKosmos.getNotificationShadeWindowModel(),
- mSecureSettings,
mKosmos::getCommunalInteractor,
mKosmos.getShadeLayoutParams());
mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {});
@@ -355,19 +348,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
}
@Test
- public void setKeyguardShowingWithSecureWindowsDisabled_disablesSecureFlag() {
- mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 1);
- mNotificationShadeWindowController.setBouncerShowing(true);
-
- verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
- assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue();
- assertThat(
- (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_PRIVACY)
- != 0)
- .isTrue();
- }
-
- @Test
public void setKeyguardNotShowing_disablesSecureFlag() {
mNotificationShadeWindowController.setBouncerShowing(false);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index fad66581682f..0642467a001b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -19,6 +19,8 @@ package com.android.systemui.shared.clocks
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
@@ -29,6 +31,7 @@ import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.ThemeConfig
+import com.android.systemui.shared.Flags
import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -103,6 +106,26 @@ class DefaultClockProviderTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_AMBIENT_AOD)
+ fun defaultClock_initialize_flagOff() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+
+ clock.initialize(true, 0f, 0f, null)
+
+ // This is the default darkTheme color
+ val expectedColor = context.resources.getColor(android.R.color.system_accent1_100)
+ verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor)
+ verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor)
+ verify(mockSmallClockView).onTimeZoneChanged(notNull())
+ verify(mockLargeClockView).onTimeZoneChanged(notNull())
+ verify(mockSmallClockView).refreshTime()
+ verify(mockLargeClockView).refreshTime()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AMBIENT_AOD)
fun defaultClock_initialize() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
@@ -110,7 +133,7 @@ class DefaultClockProviderTest : SysuiTestCase() {
clock.initialize(true, 0f, 0f, null)
- val expectedColor = 0
+ val expectedColor = Color.MAGENTA
verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor)
verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor)
verify(mockSmallClockView).onTimeZoneChanged(notNull())
@@ -165,8 +188,10 @@ class DefaultClockProviderTest : SysuiTestCase() {
}
@Test
- fun defaultClock_events_onThemeChanged_noSeed() {
- val expectedColor = 0
+ @DisableFlags(Flags.FLAG_AMBIENT_AOD)
+ fun defaultClock_events_onThemeChanged_noSeed_flagOff() {
+ // This is the default darkTheme color
+ val expectedColor = context.resources.getColor(android.R.color.system_accent1_100)
val clock = provider.createClock(DEFAULT_CLOCK_ID)
verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
@@ -180,6 +205,22 @@ class DefaultClockProviderTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_AMBIENT_AOD)
+ fun defaultClock_events_onThemeChanged_noSeedn() {
+ val expectedColor = Color.TRANSPARENT
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+
+ verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+
+ clock.smallClock.events.onThemeChanged(ThemeConfig(true, null))
+ clock.largeClock.events.onThemeChanged(ThemeConfig(true, null))
+
+ verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
+ }
+
+ @Test
fun defaultClock_events_onThemeChanged_newSeed() {
val initSeedColor = 10
val newSeedColor = 20
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 96c4a59f752d..670ebadcf5a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -62,6 +62,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.data.repository.addNotifs
+import com.android.systemui.statusbar.notification.data.repository.removeNotif
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -349,6 +350,36 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
.isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
}
+ @EnableChipsModernization
+ @Test
+ fun chips_threeChips_isSmallPortrait_allSquished() =
+ kosmos.runTest {
+ screenRecordState.value = ScreenRecordModel.Recording
+ addOngoingCallState(key = "call")
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.shortCriticalText = "Some text here"
+ }
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+
+ val latest by collectLastValue(underTest.chips)
+
+ // Squished chips are icon only
+ assertThat(latest!!.active[0])
+ .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat(latest!!.active[1])
+ .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat(latest!!.active[2])
+ .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ }
+
@DisableChipsModernization
@Test
fun chipsLegacy_countdownChipAndTimerChip_countdownNotSquished_butTimerSquished() =
@@ -851,7 +882,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
@EnableChipsModernization
@Test
- fun chips_threePromotedNotifs_topTwoActiveThirdInOverflow() =
+ fun chips_fourPromotedNotifs_topThreeActiveFourthInOverflow() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val unused by collectLastValue(underTest.chipsLegacy)
@@ -859,6 +890,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val firstIcon = createStatusBarIconViewOrNull()
val secondIcon = createStatusBarIconViewOrNull()
val thirdIcon = createStatusBarIconViewOrNull()
+ val fourthIcon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
@@ -879,20 +911,27 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
promotedContent =
PromotedNotificationContentModel.Builder("thirdNotif").build(),
),
+ activeNotificationModel(
+ key = "fourthNotif",
+ statusBarChipIcon = thirdIcon,
+ promotedContent =
+ PromotedNotificationContentModel.Builder("fourthNotif").build(),
+ ),
)
)
- assertThat(latest!!.active.size).isEqualTo(2)
+ assertThat(latest!!.active.size).isEqualTo(3)
assertIsNotifChip(latest!!.active[0], context, firstIcon, "firstNotif")
assertIsNotifChip(latest!!.active[1], context, secondIcon, "secondNotif")
+ assertIsNotifChip(latest!!.active[2], context, thirdIcon, "thirdNotif")
assertThat(latest!!.overflow.size).isEqualTo(1)
- assertIsNotifChip(latest!!.overflow[0], context, thirdIcon, "thirdNotif")
+ assertIsNotifChip(latest!!.overflow[0], context, fourthIcon, "fourthNotif")
assertThat(latest!!.inactive.size).isEqualTo(4)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
}
@Test
- fun visibleChipKeys_threePromotedNotifs_topTwoInList() =
+ fun visibleChipKeys_fourPromotedNotifs_topThreeInList() =
kosmos.runTest {
val latest by collectLastValue(underTest.visibleChipKeys)
@@ -916,10 +955,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
promotedContent =
PromotedNotificationContentModel.Builder("thirdNotif").build(),
),
+ activeNotificationModel(
+ key = "fourthNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent =
+ PromotedNotificationContentModel.Builder("fourthNotif").build(),
+ ),
)
)
- assertThat(latest).containsExactly("firstNotif", "secondNotif").inOrder()
+ assertThat(latest).containsExactly("firstNotif", "secondNotif", "thirdNotif").inOrder()
}
@DisableChipsModernization
@@ -957,7 +1002,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
@EnableChipsModernization
@Test
- fun chips_callAndPromotedNotifs_callAndFirstNotifActiveSecondNotifInOverflow() =
+ fun chips_callAndPromotedNotifs_callAndFirstTwoNotifsActive_thirdNotifInOverflow() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val unused by collectLastValue(underTest.chipsLegacy)
@@ -965,6 +1010,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val callNotificationKey = "call"
val firstIcon = createStatusBarIconViewOrNull()
val secondIcon = createStatusBarIconViewOrNull()
+ val thirdIcon = createStatusBarIconViewOrNull()
addOngoingCallState(key = callNotificationKey)
activeNotificationListRepository.addNotifs(
listOf(
@@ -980,21 +1026,28 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
promotedContent =
PromotedNotificationContentModel.Builder("secondNotif").build(),
),
+ activeNotificationModel(
+ key = "thirdNotif",
+ statusBarChipIcon = thirdIcon,
+ promotedContent =
+ PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ ),
)
)
- assertThat(latest!!.active.size).isEqualTo(2)
+ assertThat(latest!!.active.size).isEqualTo(3)
assertIsCallChip(latest!!.active[0], callNotificationKey, context)
assertIsNotifChip(latest!!.active[1], context, firstIcon, "firstNotif")
+ assertIsNotifChip(latest!!.active[2], context, secondIcon, "secondNotif")
assertThat(latest!!.overflow.size).isEqualTo(1)
- assertIsNotifChip(latest!!.overflow[0], context, secondIcon, "secondNotif")
+ assertIsNotifChip(latest!!.overflow[0], context, thirdIcon, "thirdNotif")
assertThat(latest!!.inactive.size).isEqualTo(3)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
}
@DisableChipsModernization
@Test
- fun chipsLegacy_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
+ fun chipsLegacy_screenRecordAndCallAndPromotedNotif_notifNotShown() =
kosmos.runTest {
val callNotificationKey = "call"
val latest by collectLastValue(underTest.chipsLegacy)
@@ -1016,7 +1069,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun visibleChipKeys_screenRecordAndCallAndPromotedNotifs_topTwoInList() =
+ fun visibleChipKeys_screenRecordAndCallAndPromotedNotifs_topThreeInList() =
kosmos.runTest {
val latest by collectLastValue(underTest.visibleChipKeys)
@@ -1025,20 +1078,27 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
screenRecordState.value = ScreenRecordModel.Recording
activeNotificationListRepository.addNotif(
activeNotificationModel(
- key = "notif",
+ key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ )
+ )
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
)
)
assertThat(latest)
- .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey)
+ .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey, "notif1")
.inOrder()
}
@EnableChipsModernization
@Test
- fun chips_screenRecordAndCallAndPromotedNotif_notifInOverflow() =
+ fun chips_screenRecordAndCallAndPromotedNotifs_secondNotifInOverflow() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val unused by collectLastValue(underTest.chipsLegacy)
@@ -1055,11 +1115,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
addOngoingCallState(key = callNotificationKey)
- assertThat(latest!!.active.size).isEqualTo(2)
+ // This is the overflow notif
+ val notifIcon2 = createStatusBarIconViewOrNull()
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = notifIcon2,
+ promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ )
+ )
+
+ assertThat(latest!!.active.size).isEqualTo(3)
assertIsScreenRecordChip(latest!!.active[0])
assertIsCallChip(latest!!.active[1], callNotificationKey, context)
+ assertIsNotifChip(latest!!.active[2], context, notifIcon, "notif")
assertThat(latest!!.overflow.size).isEqualTo(1)
- assertIsNotifChip(latest!!.overflow[0], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.overflow[0], context, notifIcon2, "notif2")
assertThat(latest!!.inactive.size).isEqualTo(2)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
}
@@ -1234,15 +1305,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
@Test
fun chips_movesChipsAroundAccordingToPriority() =
kosmos.runTest {
+ systemClock.setCurrentTimeMillis(10_000)
val callNotificationKey = "call"
// Start with just the lowest priority chip active
- val notifIcon = createStatusBarIconViewOrNull()
+ val notif1Icon = createStatusBarIconViewOrNull()
setNotifs(
listOf(
activeNotificationModel(
- key = "notif",
- statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ key = "notif1",
+ statusBarChipIcon = notif1Icon,
+ promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
)
)
)
@@ -1254,7 +1326,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val unused by collectLastValue(underTest.chipsLegacy)
assertThat(latest!!.active.size).isEqualTo(1)
- assertIsNotifChip(latest!!.active[0], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.active[0], context, notif1Icon, "notif1")
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(4)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -1262,10 +1334,10 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// WHEN the higher priority call chip is added
addOngoingCallState(key = callNotificationKey)
- // THEN the higher priority call chip and notif are active in that order
+ // THEN the higher priority call chip and notif1 are active in that order
assertThat(latest!!.active.size).isEqualTo(2)
assertIsCallChip(latest!!.active[0], callNotificationKey, context)
- assertIsNotifChip(latest!!.active[1], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.active[1], context, notif1Icon, "notif1")
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(3)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -1278,56 +1350,63 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
createTask(taskId = 1),
)
- // THEN the higher priority media projection chip and call are active in that order, and
- // notif is demoted to overflow
- assertThat(latest!!.active.size).isEqualTo(2)
+ // THEN media projection, then call, then notif1 are active
+ assertThat(latest!!.active.size).isEqualTo(3)
assertIsShareToAppChip(latest!!.active[0])
assertIsCallChip(latest!!.active[1], callNotificationKey, context)
- assertThat(latest!!.overflow.size).isEqualTo(1)
- assertIsNotifChip(latest!!.overflow[0], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.active[2], context, notif1Icon, "notif1")
+ assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(2)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
- // WHEN the higher priority screen record chip is added
+ // WHEN the screen record chip is added, which replaces media projection
screenRecordState.value = ScreenRecordModel.Recording
+ // AND another notification is added
+ systemClock.advanceTime(2_000)
+ val notif2Icon = createStatusBarIconViewOrNull()
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = notif2Icon,
+ promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ )
+ )
- // THEN the higher priority screen record chip and call are active in that order, and
- // media projection and notif are demoted in overflow
- assertThat(latest!!.active.size).isEqualTo(2)
+ // THEN screen record, then call, then notif2 are active
+ assertThat(latest!!.active.size).isEqualTo(3)
assertIsScreenRecordChip(latest!!.active[0])
assertIsCallChip(latest!!.active[1], callNotificationKey, context)
+ assertIsNotifChip(latest!!.active[2], context, notif2Icon, "notif2")
+
+ // AND notif1 and media projection is demoted in overflow
assertThat(latest!!.overflow.size).isEqualTo(2)
assertIsShareToAppChip(latest!!.overflow[0])
- assertIsNotifChip(latest!!.overflow[1], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.overflow[1], context, notif1Icon, "notif1")
assertThat(latest!!.inactive.size).isEqualTo(1)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
- // WHEN screen record and call is dropped
+ // WHEN screen record and call are dropped
screenRecordState.value = ScreenRecordModel.DoingNothing
- setNotifs(
- listOf(
- activeNotificationModel(
- key = "notif",
- statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
- )
- )
- )
+ removeOngoingCallState(callNotificationKey)
- // THEN media projection and notif remain
- assertThat(latest!!.active.size).isEqualTo(2)
+ // THEN media projection, notif2, and notif1 remain
+ assertThat(latest!!.active.size).isEqualTo(3)
assertIsShareToAppChip(latest!!.active[0])
- assertIsNotifChip(latest!!.active[1], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.active[1], context, notif2Icon, "notif2")
+ assertIsNotifChip(latest!!.active[2], context, notif1Icon, "notif1")
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(3)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
// WHEN media projection is dropped
mediaProjectionState.value = MediaProjectionState.NotProjecting
+ // AND notif2 is dropped
+ systemClock.advanceTime(2_000)
+ activeNotificationListRepository.removeNotif("notif2")
- // THEN only notif is active
+ // THEN only notif1 is active
assertThat(latest!!.active.size).isEqualTo(1)
- assertIsNotifChip(latest!!.active[0], context, notifIcon, "notif")
+ assertIsNotifChip(latest!!.active[0], context, notif1Icon, "notif1")
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(4)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt
new file mode 100644
index 000000000000..e4b2e6c9b359
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.app.Notification
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.view.MotionEvent
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.stub
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import org.junit.Before
+
+private fun getCloseButton(row: ExpandableNotificationRow): View {
+ val contractedView = row.showingLayout?.contractedChild!!
+ return contractedView.findViewById(com.android.internal.R.id.close_button)
+}
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationCloseButtonTest : SysuiTestCase() {
+ private lateinit var helper: NotificationTestHelper
+
+ @Before
+ fun setUp() {
+ helper = NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this)
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS)
+ fun verifyWhenFeatureDisabled() {
+ // Enable the notification row to dismiss.
+ helper.dismissibilityProvider.stub {
+ on { isDismissable(any()) } doReturn true
+ }
+
+ // By default, the close button should be gone.
+ val row = createNotificationRow()
+ val closeButton = getCloseButton(row)
+ assertThat(closeButton).isNotNull()
+ assertThat(closeButton.visibility).isEqualTo(View.GONE)
+
+ val hoverEnterEvent = MotionEvent.obtain(
+ 0/*downTime=*/,
+ 0/*eventTime=*/,
+ MotionEvent.ACTION_HOVER_ENTER,
+ 0f/*x=*/,
+ 0f/*y=*/,
+ 0/*metaState*/
+ )
+
+ // The close button should not show if the feature is disabled.
+ row.onInterceptHoverEvent(hoverEnterEvent)
+ assertThat(closeButton.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS)
+ fun verifyOnDismissableNotification() {
+ // Enable the notification row to dismiss.
+ helper.dismissibilityProvider.stub {
+ on { isDismissable(any()) } doReturn true
+ }
+
+ // By default, the close button should be gone.
+ val row = createNotificationRow()
+ val closeButton = getCloseButton(row)
+ assertThat(closeButton).isNotNull()
+ assertThat(closeButton.visibility).isEqualTo(View.GONE)
+
+ val hoverEnterEvent = MotionEvent.obtain(
+ 0/*downTime=*/,
+ 0/*eventTime=*/,
+ MotionEvent.ACTION_HOVER_ENTER,
+ 0f/*x=*/,
+ 0f/*y=*/,
+ 0/*metaState*/
+ )
+
+ // When the row is hovered, the close button should show.
+ row.onInterceptHoverEvent(hoverEnterEvent)
+ assertThat(closeButton.visibility).isEqualTo(View.VISIBLE)
+
+ val hoverExitEvent = MotionEvent.obtain(
+ 0/*downTime=*/,
+ 0/*eventTime=*/,
+ MotionEvent.ACTION_HOVER_EXIT,
+ 0f/*x=*/,
+ 0f/*y=*/,
+ 0/*metaState*/
+ )
+
+ // When hover exits the row, the close button should be gone again.
+ row.onInterceptHoverEvent(hoverExitEvent)
+ assertThat(closeButton.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS)
+ fun verifyOnUndismissableNotification() {
+ // By default, the close button should be gone.
+ val row = createNotificationRow()
+ val closeButton = getCloseButton(row)
+ assertThat(closeButton).isNotNull()
+ assertThat(closeButton.visibility).isEqualTo(View.GONE)
+
+ val hoverEnterEvent = MotionEvent.obtain(
+ 0/*downTime=*/,
+ 0/*eventTime=*/,
+ MotionEvent.ACTION_HOVER_ENTER,
+ 0f/*x=*/,
+ 0f/*y=*/,
+ 0/*metaState*/
+ )
+
+ // Because the host notification cannot be dismissed, the close button should not show.
+ row.onInterceptHoverEvent(hoverEnterEvent)
+ assertThat(closeButton.visibility).isEqualTo(View.GONE)
+ }
+
+ private fun createNotificationRow(): ExpandableNotificationRow {
+ val notification = Notification.Builder(context, "channel")
+ .setContentTitle("title")
+ .setContentText("text")
+ .setSmallIcon(R.drawable.ic_person)
+ .build()
+
+ return helper.createRow(notification)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
index 029e54658f60..20ee6c120ee8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
@@ -13,6 +13,7 @@
*/
package com.android.systemui.plugins.clocks
+import android.content.Context
import android.graphics.Rect
import com.android.systemui.plugins.annotations.ProtectedInterface
@@ -60,4 +61,12 @@ data class ThemeConfig(
* value denotes that we should use the seed color for the current system theme.
*/
val seedColor: Int?,
-)
+) {
+ fun getDefaultColor(context: Context): Int {
+ return when {
+ seedColor != null -> seedColor!!
+ isDarkTheme -> context.resources.getColor(android.R.color.system_accent1_100)
+ else -> context.resources.getColor(android.R.color.system_accent2_600)
+ }
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
index f920b187e7e5..f59dda049aa1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
@@ -24,6 +24,8 @@ data class WeatherData(
@VisibleForTesting const val TEMPERATURE_KEY = "temperature"
private const val INVALID_WEATHER_ICON_STATE = -1
+ @JvmStatic
+ @JvmOverloads
fun fromBundle(extras: Bundle, touchAction: WeatherTouchAction? = null): WeatherData? {
val description = extras.getString(DESCRIPTION_KEY)
val state =
@@ -46,7 +48,7 @@ data class WeatherData(
state = state,
useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
temperature = temperature,
- touchAction = touchAction
+ touchAction = touchAction,
)
if (DEBUG) {
Log.i(TAG, "Weather data parsed $result from $extras")
@@ -87,53 +89,53 @@ data class WeatherData(
}
// Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
- enum class WeatherStateIcon(val id: Int) {
- UNKNOWN_ICON(0),
+ enum class WeatherStateIcon(val id: Int, val icon: String) {
+ UNKNOWN_ICON(0, ""),
// Clear, day & night.
- SUNNY(1),
- CLEAR_NIGHT(2),
+ SUNNY(1, "a"),
+ CLEAR_NIGHT(2, "f"),
// Mostly clear, day & night.
- MOSTLY_SUNNY(3),
- MOSTLY_CLEAR_NIGHT(4),
+ MOSTLY_SUNNY(3, "b"),
+ MOSTLY_CLEAR_NIGHT(4, "n"),
// Partly cloudy, day & night.
- PARTLY_CLOUDY(5),
- PARTLY_CLOUDY_NIGHT(6),
+ PARTLY_CLOUDY(5, "b"),
+ PARTLY_CLOUDY_NIGHT(6, "n"),
// Mostly cloudy, day & night.
- MOSTLY_CLOUDY_DAY(7),
- MOSTLY_CLOUDY_NIGHT(8),
- CLOUDY(9),
- HAZE_FOG_DUST_SMOKE(10),
- DRIZZLE(11),
- HEAVY_RAIN(12),
- SHOWERS_RAIN(13),
+ MOSTLY_CLOUDY_DAY(7, "e"),
+ MOSTLY_CLOUDY_NIGHT(8, "e"),
+ CLOUDY(9, "e"),
+ HAZE_FOG_DUST_SMOKE(10, "d"),
+ DRIZZLE(11, "c"),
+ HEAVY_RAIN(12, "c"),
+ SHOWERS_RAIN(13, "c"),
// Scattered showers, day & night.
- SCATTERED_SHOWERS_DAY(14),
- SCATTERED_SHOWERS_NIGHT(15),
+ SCATTERED_SHOWERS_DAY(14, "c"),
+ SCATTERED_SHOWERS_NIGHT(15, "c"),
// Isolated scattered thunderstorms, day & night.
- ISOLATED_SCATTERED_TSTORMS_DAY(16),
- ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
- STRONG_TSTORMS(18),
- BLIZZARD(19),
- BLOWING_SNOW(20),
- FLURRIES(21),
- HEAVY_SNOW(22),
+ ISOLATED_SCATTERED_TSTORMS_DAY(16, "i"),
+ ISOLATED_SCATTERED_TSTORMS_NIGHT(17, "i"),
+ STRONG_TSTORMS(18, "i"),
+ BLIZZARD(19, "j"),
+ BLOWING_SNOW(20, "j"),
+ FLURRIES(21, "h"),
+ HEAVY_SNOW(22, "j"),
// Scattered snow showers, day & night.
- SCATTERED_SNOW_SHOWERS_DAY(23),
- SCATTERED_SNOW_SHOWERS_NIGHT(24),
- SNOW_SHOWERS_SNOW(25),
- MIXED_RAIN_HAIL_RAIN_SLEET(26),
- SLEET_HAIL(27),
- TORNADO(28),
- TROPICAL_STORM_HURRICANE(29),
- WINDY_BREEZY(30),
- WINTRY_MIX_RAIN_SNOW(31);
+ SCATTERED_SNOW_SHOWERS_DAY(23, "h"),
+ SCATTERED_SNOW_SHOWERS_NIGHT(24, "h"),
+ SNOW_SHOWERS_SNOW(25, "g"),
+ MIXED_RAIN_HAIL_RAIN_SLEET(26, "h"),
+ SLEET_HAIL(27, "h"),
+ TORNADO(28, "l"),
+ TROPICAL_STORM_HURRICANE(29, "m"),
+ WINDY_BREEZY(30, "k"),
+ WINTRY_MIX_RAIN_SNOW(31, "h");
companion object {
fun fromInt(value: Int) = values().firstOrNull { it.id == value }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 140db7b7a0b7..62a98d7a48ea 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -377,12 +377,15 @@ constructor(
MutableStateFlow(false)
}
- val inAllowedKeyguardState =
- keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
- it.to == KeyguardState.LOCKSCREEN || it.to == KeyguardState.GLANCEABLE_HUB
- }
-
- allOf(inAllowedDeviceState, inAllowedKeyguardState)
+ if (v2FlagEnabled()) {
+ val inAllowedKeyguardState =
+ keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
+ it.to == KeyguardState.LOCKSCREEN || it.to == KeyguardState.GLANCEABLE_HUB
+ }
+ allOf(inAllowedDeviceState, inAllowedKeyguardState)
+ } else {
+ inAllowedDeviceState
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 69da67e055fe..1e7bec257432 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -68,10 +68,4 @@ constructor(
emptyFlow()
}
}
-
- /** Triggered if a face failure occurs regardless of the mode. */
- val faceFailure: Flow<FailedFaceAuthenticationStatus> =
- deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
- FailedFaceAuthenticationStatus
- >()
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index 38e0503440f9..09936839c590 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardBypassInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -49,8 +48,6 @@ class DeviceEntryHapticsInteractor
constructor(
biometricSettingsRepository: BiometricSettingsRepository,
deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor,
- deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
- keyguardBypassInteractor: KeyguardBypassInteractor,
deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
fingerprintPropertyRepository: FingerprintPropertyRepository,
@@ -83,7 +80,12 @@ constructor(
emit(recentPowerButtonPressThresholdMs * -1L - 1L)
}
- private val playHapticsOnDeviceEntry: Flow<Boolean> =
+ /**
+ * Indicates when success haptics should play when the device is entered. This always occurs on
+ * successful fingerprint authentications. It also occurs on successful face authentication but
+ * only if the lockscreen is bypassed.
+ */
+ val playSuccessHapticOnDeviceEntry: Flow<Unit> =
deviceEntrySourceInteractor.deviceEntryFromBiometricSource
.sample(
combine(
@@ -93,29 +95,17 @@ constructor(
::Triple,
)
)
- .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
+ .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
val sideFpsAllowsHaptic =
!powerButtonDown &&
systemClock.uptimeMillis() - lastPowerButtonWakeup >
recentPowerButtonPressThresholdMs
val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic
if (!allowHaptic) {
- logger.d(
- "Skip success entry haptic from power button. Recent power button press or button is down."
- )
+ logger.d("Skip success haptic. Recent power button press or button is down.")
}
allowHaptic
}
-
- private val playHapticsOnFaceAuthSuccessAndBypassDisabled: Flow<Boolean> =
- deviceEntryFaceAuthInteractor.isAuthenticated
- .filter { it }
- .sample(keyguardBypassInteractor.isBypassAvailable)
- .map { !it }
-
- val playSuccessHaptic: Flow<Unit> =
- merge(playHapticsOnDeviceEntry, playHapticsOnFaceAuthSuccessAndBypassDisabled)
- .filter { it }
// map to Unit
.map {}
.dumpWhileCollecting("playSuccessHaptic")
@@ -123,7 +113,7 @@ constructor(
private val playErrorHapticForBiometricFailure: Flow<Unit> =
merge(
deviceEntryFingerprintAuthInteractor.fingerprintFailure,
- deviceEntryBiometricAuthInteractor.faceFailure,
+ deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
)
// map to Unit
.map {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 45801ba3517a..aeb327035c79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -333,7 +333,7 @@ object KeyguardRootViewBinder {
if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
launch {
- deviceEntryHapticsInteractor.playSuccessHaptic.collect {
+ deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry.collect {
if (msdlFeedback()) {
msdlPlayer?.playToken(
MSDLToken.UNLOCK,
@@ -474,7 +474,7 @@ object KeyguardRootViewBinder {
val transition = blueprintViewModel.currentTransition.value
val shouldAnimate = transition != null && transition.config.type.animateNotifChanges
if (prevTransition == transition && shouldAnimate) {
- logger.w("Skipping; layout during transition")
+ logger.w("Skipping onNotificationContainerBoundsChanged during transition")
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index faa6c52162ce..a85b9b04c1ce 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -327,7 +327,8 @@ public class LogModule {
@SysUISingleton
@KeyguardBlueprintLog
public static LogBuffer provideKeyguardBlueprintLog(LogBufferFactory factory) {
- return factory.create("KeyguardBlueprintLog", 100);
+ // TODO(b/389987229): Reduce back to 100
+ return factory.create("KeyguardBlueprintLog", 1000);
}
/**
@@ -337,7 +338,8 @@ public class LogModule {
@SysUISingleton
@KeyguardClockLog
public static LogBuffer provideKeyguardClockLog(LogBufferFactory factory) {
- return factory.create("KeyguardClockLog", 100);
+ // TODO(b/389987229): Reduce back to 100
+ return factory.create("KeyguardClockLog", 1000);
}
/**
@@ -347,7 +349,8 @@ public class LogModule {
@SysUISingleton
@KeyguardSmallClockLog
public static LogBuffer provideKeyguardSmallClockLog(LogBufferFactory factory) {
- return factory.create("KeyguardSmallClockLog", 100);
+ // TODO(b/389987229): Reduce back to 100
+ return factory.create("KeyguardSmallClockLog", 1000);
}
/**
@@ -357,7 +360,8 @@ public class LogModule {
@SysUISingleton
@KeyguardLargeClockLog
public static LogBuffer provideKeyguardLargeClockLog(LogBufferFactory factory) {
- return factory.create("KeyguardLargeClockLog", 100);
+ // TODO(b/389987229): Reduce back to 100
+ return factory.create("KeyguardLargeClockLog", 1000);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index 9d375809786a..02dce406bbee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -698,6 +698,27 @@ public class MediaSwitchingController
List<MediaItem> finalMediaItems = targetMediaDevices.stream()
.map(MediaItem::createDeviceMediaItem)
.collect(Collectors.toList());
+
+ boolean shouldAddFirstSeenSelectedDevice =
+ com.android.media.flags.Flags.enableOutputSwitcherDeviceGrouping();
+
+ if (shouldAddFirstSeenSelectedDevice) {
+ finalMediaItems.clear();
+ Set<String> selectedDevicesIds = getSelectedMediaDevice().stream()
+ .map(MediaDevice::getId)
+ .collect(Collectors.toSet());
+ for (MediaDevice targetMediaDevice : targetMediaDevices) {
+ if (shouldAddFirstSeenSelectedDevice
+ && selectedDevicesIds.contains(targetMediaDevice.getId())) {
+ finalMediaItems.add(MediaItem.createDeviceMediaItem(
+ targetMediaDevice, /* isFirstDeviceInGroup */ true));
+ shouldAddFirstSeenSelectedDevice = false;
+ } else {
+ finalMediaItems.add(MediaItem.createDeviceMediaItem(
+ targetMediaDevice, /* isFirstDeviceInGroup */ false));
+ }
+ }
+ }
dividerItems.forEach(finalMediaItems::add);
attachConnectNewDeviceItemIfNeeded(finalMediaItems);
return finalMediaItems;
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 4753b9ac0457..7bb831baec20 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -687,7 +687,7 @@ constructor(
if (!isDeviceEntered) {
coroutineScope {
launch {
- deviceEntryHapticsInteractor.playSuccessHaptic
+ deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry
.sample(sceneInteractor.currentScene)
.collect { currentScene ->
if (Flags.msdlFeedback()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 305444f7ab5e..fa17b4fad592 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -30,8 +30,6 @@ import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.view.Display;
import android.view.IWindow;
@@ -75,7 +73,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.settings.SecureSettings;
import dagger.Lazy;
@@ -134,7 +131,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private final SysuiColorExtractor mColorExtractor;
private final NotificationShadeWindowModel mNotificationShadeWindowModel;
- private final SecureSettings mSecureSettings;
/**
* Layout params would be aggregated and dispatched all at once if this is > 0.
*
@@ -168,7 +164,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
Lazy<SelectedUserInteractor> userInteractor,
UserTracker userTracker,
NotificationShadeWindowModel notificationShadeWindowModel,
- SecureSettings secureSettings,
Lazy<CommunalInteractor> communalInteractor,
@ShadeDisplayAware LayoutParams shadeWindowLayoutParams) {
mContext = context;
@@ -186,7 +181,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
mNotificationShadeWindowModel = notificationShadeWindowModel;
- mSecureSettings = secureSettings;
// prefix with {slow} to make sure this dumps at the END of the critical section.
dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
mAuthController = authController;
@@ -424,7 +418,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
(long) mLpChanged.preferredMaxDisplayRefreshRate);
}
- if (state.bouncerShowing && !isSecureWindowsDisabled()) {
+ if (state.bouncerShowing) {
mLpChanged.flags |= LayoutParams.FLAG_SECURE;
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_SECURE;
@@ -437,13 +431,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private boolean isSecureWindowsDisabled() {
- return mSecureSettings.getIntForUser(
- Settings.Secure.DISABLE_SECURE_WINDOWS,
- 0,
- UserHandle.USER_CURRENT) == 1;
- }
-
private void adjustScreenOrientation(NotificationShadeWindowState state) {
if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) {
if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index eae2c25d77d8..8228b5533fca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -555,7 +555,6 @@ constructor(
secondary = DEFAULT_INTERNAL_INACTIVE_MODEL,
)
- // TODO(b/392886257): Support 3 chips if there's space available.
- private const val MAX_VISIBLE_CHIPS = 2
+ private const val MAX_VISIBLE_CHIPS = 3
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6837cb2a6292..76d8fb8d3c15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -129,8 +129,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
private void updateColors() {
- if (usesTransparentBackground()) {
- mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ if (notificationRowTransparency()) {
+ if (mIsBlurSupported) {
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ } else {
+ mNormalColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
+ }
} else {
mNormalColor = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceContainerHigh);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index e8054c07eac8..8105ae0960ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -283,7 +283,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
() -> mSelectedUserInteractor,
mock(UserTracker.class),
mKosmos.getNotificationShadeWindowModel(),
- mSecureSettings,
mKosmos::getCommunalInteractor,
mKosmos.getShadeLayoutParams());
mFeatureFlags = new FakeFeatureFlags();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 88c2697fe2f2..5c26dac5eb30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -1573,6 +1573,25 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
assertThat(items.get(1).isFirstDeviceInGroup()).isFalse();
}
+ @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+ @Test
+ public void deviceListUpdateWithDifferentDevices_firstSelectedDeviceIsFirstDeviceInGroup() {
+ when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true);
+ doReturn(mMediaDevices)
+ .when(mLocalMediaManager)
+ .getSelectedMediaDevice();
+ mMediaSwitchingController.start(mCb);
+ reset(mCb);
+ mMediaSwitchingController.getMediaItemList().clear();
+ mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+ mMediaDevices.clear();
+ mMediaDevices.add(mMediaDevice2);
+ mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+ List<MediaItem> items = mMediaSwitchingController.getMediaItemList();
+ assertThat(items.get(0).isFirstDeviceInGroup()).isTrue();
+ }
+
private int getNumberOfConnectDeviceButtons() {
int numberOfConnectDeviceButtons = 0;
for (MediaItem item : mMediaSwitchingController.getMediaItemList()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 341bd3a38999..a978ecdb3534 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,8 @@ import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR;
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -55,8 +57,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
@@ -136,7 +136,6 @@ import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -166,7 +165,6 @@ import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvision
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.settings.FakeGlobalSettings;
-import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.Flags;
@@ -451,7 +449,6 @@ public class BubblesTest extends SysuiTestCase {
() -> mSelectedUserInteractor,
mUserTracker,
mNotificationShadeWindowModel,
- new FakeSettings(),
mKosmos::getCommunalInteractor,
mKosmos.getShadeLayoutParams()
);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
index 6f570a86b19e..cd4b09c5267a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
@@ -21,7 +21,6 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi
import com.android.systemui.dump.dumpManager
import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardBypassInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.util.time.systemClock
@@ -31,8 +30,6 @@ val Kosmos.deviceEntryHapticsInteractor by
DeviceEntryHapticsInteractor(
biometricSettingsRepository = biometricSettingsRepository,
deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor,
- deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
- keyguardBypassInteractor = keyguardBypassInteractor,
deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
deviceEntrySourceInteractor = deviceEntrySourceInteractor,
fingerprintPropertyRepository = fingerprintPropertyRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt
index e639326bd7a1..0e348c88f058 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt
@@ -24,21 +24,23 @@ import org.junit.runners.model.Statement
class LogWtfHandlerRule : TestRule {
- private var started = false
- private var handler = ThrowAndFailAtEnd
+ private var failureLogExemptions = mutableListOf<FailureLogExemption>()
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
- started = true
+ val handler = TerribleFailureTestHandler()
val originalWtfHandler = Log.setWtfHandler(handler)
var failure: Throwable? = null
try {
base.evaluate()
} catch (ex: Throwable) {
- failure = ex.runAndAddSuppressed { handler.onTestFailure(ex) }
+ failure = ex
} finally {
- failure = failure.runAndAddSuppressed { handler.onTestFinished() }
+ failure =
+ runAndAddSuppressed(failure) {
+ handler.onTestFinished(failureLogExemptions)
+ }
Log.setWtfHandler(originalWtfHandler)
}
if (failure != null) {
@@ -48,74 +50,52 @@ class LogWtfHandlerRule : TestRule {
}
}
- fun Throwable?.runAndAddSuppressed(block: () -> Unit): Throwable? {
+ /** Adds a log failure exemption. Exemptions are evaluated at the end of the test. */
+ fun addFailureLogExemption(exemption: FailureLogExemption) {
+ failureLogExemptions.add(exemption)
+ }
+
+ /** Clears and sets exemptions. Exemptions are evaluated at the end of the test. */
+ fun resetFailureLogExemptions(vararg exemptions: FailureLogExemption) {
+ failureLogExemptions = exemptions.toMutableList()
+ }
+
+ private fun runAndAddSuppressed(currentError: Throwable?, block: () -> Unit): Throwable? {
try {
block()
} catch (t: Throwable) {
- if (this == null) {
+ if (currentError == null) {
return t
}
- addSuppressed(t)
+ currentError.addSuppressed(t)
}
- return this
+ return currentError
}
- fun setWtfHandler(handler: TerribleFailureTestHandler) {
- check(!started) { "Should only be called before the test starts" }
- this.handler = handler
- }
-
- fun interface TerribleFailureTestHandler : TerribleFailureHandler {
- fun onTestFailure(failure: Throwable) {}
- fun onTestFinished() {}
- }
-
- companion object Handlers {
- val ThrowAndFailAtEnd
- get() =
- object : TerribleFailureTestHandler {
- val failures = mutableListOf<Log.TerribleFailure>()
-
- override fun onTerribleFailure(
- tag: String,
- what: Log.TerribleFailure,
- system: Boolean
- ) {
- failures.add(what)
- throw what
- }
+ private class TerribleFailureTestHandler : TerribleFailureHandler {
+ private val failureLogs = mutableListOf<FailureLog>()
- override fun onTestFailure(failure: Throwable) {
- super.onTestFailure(failure)
- }
+ override fun onTerribleFailure(tag: String, what: Log.TerribleFailure, system: Boolean) {
+ failureLogs.add(FailureLog(tag = tag, failure = what, system = system))
+ }
- override fun onTestFinished() {
- if (failures.isNotEmpty()) {
- throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0])
- }
- }
+ fun onTestFinished(exemptions: List<FailureLogExemption>) {
+ val failures =
+ failureLogs.filter { failureLog ->
+ !exemptions.any { it.isFailureLogExempt(failureLog) }
}
+ if (failures.isNotEmpty()) {
+ throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0].failure)
+ }
+ }
+ }
- val JustThrow = TerribleFailureTestHandler { _, what, _ -> throw what }
-
- val JustFailAtEnd
- get() =
- object : TerribleFailureTestHandler {
- val failures = mutableListOf<Log.TerribleFailure>()
-
- override fun onTerribleFailure(
- tag: String,
- what: Log.TerribleFailure,
- system: Boolean
- ) {
- failures.add(what)
- }
+ /** All the information from a call to [Log.wtf] that was handed to [TerribleFailureHandler] */
+ data class FailureLog(val tag: String, val failure: Log.TerribleFailure, val system: Boolean)
- override fun onTestFinished() {
- if (failures.isNotEmpty()) {
- throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0])
- }
- }
- }
+ /** An interface for exempting a [FailureLog] from causing a test failure. */
+ fun interface FailureLogExemption {
+ /** Determines whether a log should be except from failing the test. */
+ fun isFailureLogExempt(log: FailureLog): Boolean
}
}
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 9dd09cef88f9..40085ed89294 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -113,7 +113,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
- DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
+ accessCount);
}
/**
@@ -257,8 +257,7 @@ final class AttributedOp {
if (isStarted) {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
- attributionFlags, attributionChainId,
- DiscreteOpsRegistry.ACCESS_TYPE_START_OP, 1);
+ attributionFlags, attributionChainId, 1);
}
}
@@ -344,9 +343,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
- event.getAttributionFlags(), event.getAttributionChainId(),
- isPausing ? DiscreteOpsRegistry.ACCESS_TYPE_PAUSE_OP
- : DiscreteOpsRegistry.ACCESS_TYPE_FINISH_OP);
+ event.getAttributionFlags(), event.getAttributionChainId());
if (!isPausing) {
mAppOpsService.mInProgressStartOpEventPool.release(event);
@@ -454,7 +451,7 @@ final class AttributedOp {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), startTime, event.getAttributionFlags(),
- event.getAttributionChainId(), DiscreteOpsRegistry.ACCESS_TYPE_RESUME_OP, 1);
+ event.getAttributionChainId(), 1);
if (shouldSendActive) {
mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
index 12c35ae92cbe..b7599f6e40c3 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -46,22 +46,17 @@ import static android.app.AppOpsManager.OP_WRITE_SMS;
import static java.lang.Long.min;
import static java.lang.Math.max;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.os.AsyncTask;
import android.os.Build;
-import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Date;
@@ -134,27 +129,6 @@ abstract class DiscreteOpsRegistry {
boolean mDebugMode = false;
- static final int ACCESS_TYPE_NOTE_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
- static final int ACCESS_TYPE_START_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
- static final int ACCESS_TYPE_FINISH_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
- static final int ACCESS_TYPE_PAUSE_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
- static final int ACCESS_TYPE_RESUME_OP =
- FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
- ACCESS_TYPE_NOTE_OP,
- ACCESS_TYPE_START_OP,
- ACCESS_TYPE_FINISH_OP,
- ACCESS_TYPE_PAUSE_OP,
- ACCESS_TYPE_RESUME_OP
- })
- @interface AccessType {}
-
void systemReady() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
@@ -166,8 +140,7 @@ abstract class DiscreteOpsRegistry {
abstract void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId,
int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
- @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteOpsRegistry.AccessType int accessType);
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId);
/**
* A periodic callback from {@link AppOpsService} to flush the in memory events to disk.
@@ -227,9 +200,6 @@ abstract class DiscreteOpsRegistry {
return true;
}
- // could this be impl detail of discrete registry, just one test is using the method
- // abstract DiscreteRegistry.DiscreteOps getAllDiscreteOps();
-
private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
@@ -277,28 +247,4 @@ abstract class DiscreteOpsRegistry {
}
return result;
}
-
- /**
- * Whether app op access tacking is enabled and a metric event should be logged.
- */
- static boolean shouldLogAccess(int op) {
- return Flags.appopAccessTrackingLoggingEnabled()
- && ArrayUtils.contains(sDiscreteOpsToLog, op);
- }
-
- String getAttributionTag(String attributionTag, String packageName) {
- if (attributionTag == null || packageName == null) {
- return attributionTag;
- }
- int firstChar = 0;
- if (attributionTag.startsWith(packageName)) {
- firstChar = packageName.length();
- if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
- == '.') {
- firstChar++;
- }
- }
- return attributionTag.substring(firstChar);
- }
-
}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
index dc11be9aadb6..c897891d02c3 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -36,7 +36,6 @@ import android.util.IntArray;
import android.util.LongSparseArray;
import android.util.Slog;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
import java.io.File;
@@ -97,15 +96,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
void recordDiscreteAccess(int uid, String packageName,
@NonNull String deviceId, int op,
@Nullable String attributionTag, int flags, int uidState,
- long accessTime, long accessDuration, int attributionFlags, int attributionChainId,
- int accessType) {
- if (shouldLogAccess(op)) {
- FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
- uidState, flags, attributionFlags,
- getAttributionTag(attributionTag, packageName),
- attributionChainId);
- }
-
+ long accessTime, long accessDuration, int attributionFlags, int attributionChainId) {
if (!isDiscreteOp(op, flags)) {
return;
}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
index 1523cca86607..909a04c44ae5 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
@@ -48,15 +48,13 @@ class DiscreteOpsTestingShim extends DiscreteOpsRegistry {
@Override
void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
@Nullable String attributionTag, int flags, int uidState, long accessTime,
- long accessDuration, int attributionFlags, int attributionChainId, int accessType) {
+ long accessDuration, int attributionFlags, int attributionChainId) {
long start = SystemClock.uptimeMillis();
mXmlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
- uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
- accessType);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
long start2 = SystemClock.uptimeMillis();
mSqlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
- uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
- accessType);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
long end = SystemClock.uptimeMillis();
long xmlTimeTaken = start2 - start;
long sqlTimeTaken = end - start2;
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index a6e3fc7cc66a..20706b659ffb 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -45,7 +45,6 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -159,15 +158,7 @@ class DiscreteOpsXmlRegistry extends DiscreteOpsRegistry {
void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
- @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @AccessType int accessType) {
- if (shouldLogAccess(op)) {
- FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
- uidState, flags, attributionFlags,
- getAttributionTag(attributionTag, packageName),
- attributionChainId);
- }
-
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
if (!isDiscreteOp(op, flags)) {
return;
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index d267e0d9e536..06e43e8ec68d 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -497,7 +497,7 @@ final class HistoricalRegistry {
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long accessTime,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteOpsRegistry.AccessType int accessType, int accessCount) {
+ int accessCount) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -510,7 +510,7 @@ final class HistoricalRegistry {
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
attributionTag, flags, uidState, accessTime, -1, attributionFlags,
- attributionChainId, accessType);
+ attributionChainId);
}
}
}
@@ -533,8 +533,7 @@ final class HistoricalRegistry {
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long eventStartTime, long increment,
- @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteOpsRegistry.AccessType int accessType) {
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -546,7 +545,7 @@ final class HistoricalRegistry {
attributionTag, uidState, flags, increment);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
attributionTag, flags, uidState, eventStartTime, increment,
- attributionFlags, attributionChainId, accessType);
+ attributionFlags, attributionChainId);
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7016c11b69e7..a28069bbf050 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -674,9 +674,9 @@ public final class DisplayManagerService extends SystemService {
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
-
+ // TODO(b/400384229): stats service needs to react to mirror-extended switch
mExternalDisplayStatsService = new ExternalDisplayStatsService(mContext, mHandler,
- this::isExtendedDisplayEnabled);
+ this::isExtendedDisplayAllowed);
mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext,
mExternalDisplayStatsService);
mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector());
@@ -690,7 +690,7 @@ public final class DisplayManagerService extends SystemService {
deliverTopologyUpdate(update.first);
};
mDisplayTopologyCoordinator = new DisplayTopologyCoordinator(
- this::isExtendedDisplayEnabled, topologyChangedCallback,
+ this::isExtendedDisplayAllowed, topologyChangedCallback,
new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged);
} else {
mDisplayTopologyCoordinator = null;
@@ -2411,7 +2411,10 @@ public final class DisplayManagerService extends SystemService {
updateLogicalDisplayState(display);
}
- private boolean isExtendedDisplayEnabled() {
+ private boolean isExtendedDisplayAllowed() {
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ return true;
+ }
try {
return 0 != Settings.Global.getInt(
mContext.getContentResolver(),
@@ -6045,7 +6048,13 @@ public final class DisplayManagerService extends SystemService {
return;
}
if (inTopology) {
- mDisplayTopologyCoordinator.onDisplayAdded(getDisplayInfo(displayId));
+ var info = getDisplayInfo(displayId);
+ if (info == null) {
+ Slog.w(TAG, "onDisplayBelongToTopologyChanged: cancelled displayId="
+ + displayId + " info=null");
+ return;
+ }
+ mDisplayTopologyCoordinator.onDisplayAdded(info);
} else {
mDisplayTopologyCoordinator.onDisplayRemoved(displayId);
}
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 997fff58b952..b4df1f76dccb 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -69,9 +69,9 @@ class DisplayTopologyCoordinator {
private final SparseArray<String> mDisplayIdToUniqueIdMapping = new SparseArray<>();
/**
- * Check if extended displays are enabled. If not, a topology is not needed.
+ * Check if extended displays are allowed. If not, a topology is not needed.
*/
- private final BooleanSupplier mIsExtendedDisplayEnabled;
+ private final BooleanSupplier mIsExtendedDisplayAllowed;
/**
* Callback used to send topology updates.
@@ -83,21 +83,21 @@ class DisplayTopologyCoordinator {
private final DisplayManagerService.SyncRoot mSyncRoot;
private final Runnable mTopologySavedCallback;
- DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayEnabled,
+ DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayAllowed,
Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback,
Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
Runnable topologySavedCallback) {
- this(new Injector(), isExtendedDisplayEnabled, onTopologyChangedCallback,
+ this(new Injector(), isExtendedDisplayAllowed, onTopologyChangedCallback,
topologyChangeExecutor, syncRoot, topologySavedCallback);
}
@VisibleForTesting
- DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayEnabled,
+ DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayAllowed,
Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback,
Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
Runnable topologySavedCallback) {
mTopology = injector.getTopology();
- mIsExtendedDisplayEnabled = isExtendedDisplayEnabled;
+ mIsExtendedDisplayAllowed = isExtendedDisplayAllowed;
mOnTopologyChangedCallback = onTopologyChangedCallback;
mTopologyChangeExecutor = topologyChangeExecutor;
mSyncRoot = syncRoot;
@@ -262,9 +262,9 @@ class DisplayTopologyCoordinator {
return false;
}
if ((info.type == Display.TYPE_EXTERNAL || info.type == Display.TYPE_OVERLAY)
- && !mIsExtendedDisplayEnabled.getAsBoolean()) {
+ && !mIsExtendedDisplayAllowed.getAsBoolean()) {
Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
- + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayEnabled");
+ + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed");
return false;
}
return true;
diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java
index c4929c210e2c..b376417061db 100644
--- a/services/core/java/com/android/server/power/ScreenUndimDetector.java
+++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java
@@ -30,11 +30,11 @@ import android.provider.DeviceConfig;
import android.util.Slog;
import android.view.Display;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
/**
* Detects when user manually undims the screen (x times) and acquires a wakelock to keep the screen
@@ -48,7 +48,6 @@ public class ScreenUndimDetector {
/** DeviceConfig flag: is keep screen on feature enabled. */
static final String KEY_KEEP_SCREEN_ON_ENABLED = "keep_screen_on_enabled";
- private static final boolean DEFAULT_KEEP_SCREEN_ON_ENABLED = true;
private static final int OUTCOME_POWER_BUTTON =
FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__POWER_BUTTON;
private static final int OUTCOME_TIMEOUT =
@@ -58,15 +57,11 @@ public class ScreenUndimDetector {
/** DeviceConfig flag: how long should we keep the screen on. */
@VisibleForTesting
static final String KEY_KEEP_SCREEN_ON_FOR_MILLIS = "keep_screen_on_for_millis";
- @VisibleForTesting
- static final long DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS = TimeUnit.MINUTES.toMillis(10);
private long mKeepScreenOnForMillis;
/** DeviceConfig flag: how many user undims required to trigger keeping the screen on. */
@VisibleForTesting
static final String KEY_UNDIMS_REQUIRED = "undims_required";
- @VisibleForTesting
- static final int DEFAULT_UNDIMS_REQUIRED = 2;
private int mUndimsRequired;
/**
@@ -76,8 +71,6 @@ public class ScreenUndimDetector {
@VisibleForTesting
static final String KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS =
"max_duration_between_undims_millis";
- @VisibleForTesting
- static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS = TimeUnit.MINUTES.toMillis(5);
private long mMaxDurationBetweenUndimsMillis;
@VisibleForTesting
@@ -92,6 +85,7 @@ public class ScreenUndimDetector {
private long mUndimOccurredTime = -1;
private long mInteractionAfterUndimTime = -1;
private InternalClock mClock;
+ private Context mContext;
public ScreenUndimDetector() {
mClock = new InternalClock();
@@ -109,12 +103,13 @@ public class ScreenUndimDetector {
/** Should be called in parent's systemReady() */
public void systemReady(Context context) {
+ mContext = context;
readValuesFromDeviceConfig();
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- context.getMainExecutor(),
+ mContext.getMainExecutor(),
(properties) -> onDeviceConfigChange(properties.getKeyset()));
- final PowerManager powerManager = context.getSystemService(PowerManager.class);
+ final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE,
UNDIM_DETECTOR_WAKE_LOCK);
@@ -203,36 +198,44 @@ public class ScreenUndimDetector {
}
}
- private boolean readKeepScreenOnNotificationEnabled() {
+ private boolean readKeepScreenOnEnabled() {
+ boolean defaultKeepScreenOnEnabled = mContext.getResources().getBoolean(
+ R.bool.config_defaultPreventScreenTimeoutEnabled);
return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_KEEP_SCREEN_ON_ENABLED,
- DEFAULT_KEEP_SCREEN_ON_ENABLED);
+ defaultKeepScreenOnEnabled);
}
private long readKeepScreenOnForMillis() {
+ long defaultKeepScreenOnDuration = mContext.getResources().getInteger(
+ R.integer.config_defaultPreventScreenTimeoutForMillis);
return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_KEEP_SCREEN_ON_FOR_MILLIS,
- DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS);
+ defaultKeepScreenOnDuration);
}
private int readUndimsRequired() {
+ int defaultUndimsRequired = mContext.getResources().getInteger(
+ R.integer.config_defaultUndimsRequired);
int undimsRequired = DeviceConfig.getInt(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_UNDIMS_REQUIRED,
- DEFAULT_UNDIMS_REQUIRED);
+ defaultUndimsRequired);
if (undimsRequired < 1 || undimsRequired > 5) {
Slog.e(TAG, "Provided undimsRequired=" + undimsRequired
- + " is not allowed [1, 5]; using the default=" + DEFAULT_UNDIMS_REQUIRED);
- return DEFAULT_UNDIMS_REQUIRED;
+ + " is not allowed [1, 5]; using the default=" + defaultUndimsRequired);
+ return defaultUndimsRequired;
}
return undimsRequired;
}
private long readMaxDurationBetweenUndimsMillis() {
+ long defaultMaxDurationBetweenUndimsMillis = mContext.getResources().getInteger(
+ R.integer.config_defaultMaxDurationBetweenUndimsMillis);
return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS,
- DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS);
+ defaultMaxDurationBetweenUndimsMillis);
}
private void onDeviceConfigChange(@NonNull Set<String> keys) {
@@ -253,15 +256,16 @@ public class ScreenUndimDetector {
@VisibleForTesting
void readValuesFromDeviceConfig() {
- mKeepScreenOnEnabled = readKeepScreenOnNotificationEnabled();
+ mKeepScreenOnEnabled = readKeepScreenOnEnabled();
mKeepScreenOnForMillis = readKeepScreenOnForMillis();
mUndimsRequired = readUndimsRequired();
mMaxDurationBetweenUndimsMillis = readMaxDurationBetweenUndimsMillis();
Slog.i(TAG, "readValuesFromDeviceConfig():"
+ "\nmKeepScreenOnForMillis=" + mKeepScreenOnForMillis
- + "\nmKeepScreenOnNotificationEnabled=" + mKeepScreenOnEnabled
- + "\nmUndimsRequired=" + mUndimsRequired);
+ + "\nmKeepScreenOnEnabled=" + mKeepScreenOnEnabled
+ + "\nmUndimsRequired=" + mUndimsRequired
+ + "\nmMaxDurationBetweenUndimsMillis=" + mMaxDurationBetweenUndimsMillis);
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
index 5563f98e8842..7cd9bdbc662c 100644
--- a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
+++ b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
@@ -374,6 +374,10 @@ public class BatteryHistoryDirectory implements BatteryStatsHistory.BatteryHisto
@SuppressWarnings("unchecked")
@Override
public List<BatteryHistoryFragment> getFragments() {
+ if (!mLock.isHeldByCurrentThread()) {
+ throw new IllegalStateException("Reading battery history without a lock");
+ }
+
ensureInitialized();
return (List<BatteryHistoryFragment>)
(List<? extends BatteryHistoryFragment>) mHistoryFiles;
@@ -443,44 +447,6 @@ public class BatteryHistoryDirectory implements BatteryStatsHistory.BatteryHisto
}
@Override
- public BatteryHistoryFragment getNextFragment(BatteryHistoryFragment current, long startTimeMs,
- long endTimeMs) {
- ensureInitialized();
-
- if (!mLock.isHeldByCurrentThread()) {
- throw new IllegalStateException("Iterating battery history without a lock");
- }
-
- int nextFileIndex = 0;
- int firstFileIndex = 0;
- // skip the last file because its data is in history buffer.
- int lastFileIndex = mHistoryFiles.size() - 2;
- for (int i = lastFileIndex; i >= 0; i--) {
- BatteryHistoryFragment fragment = mHistoryFiles.get(i);
- if (current != null && fragment.monotonicTimeMs == current.monotonicTimeMs) {
- nextFileIndex = i + 1;
- }
- if (fragment.monotonicTimeMs > endTimeMs) {
- lastFileIndex = i - 1;
- }
- if (fragment.monotonicTimeMs <= startTimeMs) {
- firstFileIndex = i;
- break;
- }
- }
-
- if (nextFileIndex < firstFileIndex) {
- nextFileIndex = firstFileIndex;
- }
-
- if (nextFileIndex <= lastFileIndex) {
- return mHistoryFiles.get(nextFileIndex);
- }
-
- return null;
- }
-
- @Override
public boolean hasCompletedFragments() {
ensureInitialized();
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 21628341ea62..cb122f2080a2 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -107,8 +107,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
&& !ActivityManager.isLowRamDeviceStatic(); // Don't support Android Go
setSnapshotEnabled(snapshotEnabled);
mSnapshotPersistQueue = persistQueue;
- mPersistInfoProvider = createPersistInfoProvider(service,
- Environment::getDataSystemCeDirectory);
+ mPersistInfoProvider = createPersistInfoProvider(service);
mPersister = new TaskSnapshotPersister(
persistQueue,
mPersistInfoProvider,
@@ -117,6 +116,11 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
initialize(new ActivitySnapshotCache());
}
+ @VisibleForTesting
+ PersistInfoProvider createPersistInfoProvider(WindowManagerService service) {
+ return createPersistInfoProvider(service, Environment::getDataSystemCeDirectory);
+ }
+
@Override
protected float initSnapshotScale() {
final float config = mService.mContext.getResources().getFloat(
diff --git a/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java b/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java
index 5db02dff8351..aaa5a0074506 100644
--- a/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java
@@ -16,10 +16,20 @@
package com.android.server.wm;
+import static com.android.server.wm.AbsAppSnapshotController.TAG;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.window.TaskSnapshot;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
+
import java.io.File;
+import java.util.UUID;
class BaseAppSnapshotPersister {
static final String LOW_RES_FILE_POSTFIX = "_reduced";
@@ -29,6 +39,7 @@ class BaseAppSnapshotPersister {
// Shared with SnapshotPersistQueue
protected final Object mLock;
protected final SnapshotPersistQueue mSnapshotPersistQueue;
+ @VisibleForTesting
protected final PersistInfoProvider mPersistInfoProvider;
BaseAppSnapshotPersister(SnapshotPersistQueue persistQueue,
@@ -79,6 +90,8 @@ class BaseAppSnapshotPersister {
private final boolean mEnableLowResSnapshots;
private final float mLowResScaleFactor;
private final boolean mUse16BitFormat;
+ private final SparseBooleanArray mInitializedUsers = new SparseBooleanArray();
+ private final SparseArray<File> mScrambleDirectories = new SparseArray<>();
PersistInfoProvider(DirectoryResolver directoryResolver, String dirName,
boolean enableLowResSnapshots, float lowResScaleFactor, boolean use16BitFormat) {
@@ -91,9 +104,80 @@ class BaseAppSnapshotPersister {
@NonNull
File getDirectory(int userId) {
+ if (Flags.scrambleSnapshotFileName()) {
+ final File directory = getOrInitScrambleDirectory(userId);
+ if (directory != null) {
+ return directory;
+ }
+ }
+ return getBaseDirectory(userId);
+ }
+
+ @NonNull
+ private File getBaseDirectory(int userId) {
return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), mDirName);
}
+ @Nullable
+ private File getOrInitScrambleDirectory(int userId) {
+ synchronized (mScrambleDirectories) {
+ if (mInitializedUsers.get(userId)) {
+ return mScrambleDirectories.get(userId);
+ }
+ mInitializedUsers.put(userId, true);
+ final File scrambledDirectory = getScrambleDirectory(userId);
+ final File baseDir = getBaseDirectory(userId);
+ String newName = null;
+ // If directory exists, rename
+ if (scrambledDirectory.exists()) {
+ newName = UUID.randomUUID().toString();
+ final File scrambleTo = new File(baseDir, newName);
+ if (!scrambledDirectory.renameTo(scrambleTo)) {
+ Slog.w(TAG, "SnapshotPersister rename scramble folder fail.");
+ return null;
+ }
+ } else {
+ // If directory not exists, mkDir.
+ if (!baseDir.exists() && !baseDir.mkdir()) {
+ Slog.w(TAG, "SnapshotPersister make base folder fail.");
+ return null;
+ }
+ if (!scrambledDirectory.mkdir()) {
+ Slog.e(TAG, "SnapshotPersister make scramble folder fail");
+ return null;
+ }
+ // Move any existing files to this folder.
+ final String[] files = baseDir.list();
+ if (files != null) {
+ for (String file : files) {
+ final File original = new File(baseDir, file);
+ if (original.isDirectory()) {
+ newName = file;
+ } else {
+ File to = new File(scrambledDirectory, file);
+ original.renameTo(to);
+ }
+ }
+ }
+ }
+ final File newFolder = new File(baseDir, newName);
+ mScrambleDirectories.put(userId, newFolder);
+ return newFolder;
+ }
+ }
+
+ @NonNull
+ private File getScrambleDirectory(int userId) {
+ final File dir = getBaseDirectory(userId);
+ final String[] directories = dir.list(
+ (current, name) -> new File(current, name).isDirectory());
+ if (directories != null && directories.length > 0) {
+ return new File(dir, directories[0]);
+ } else {
+ return new File(dir, UUID.randomUUID().toString());
+ }
+ }
+
/**
* Return if task snapshots are stored in 16 bit pixel format.
*
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index fa7a99d55896..d90fff229cd9 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -40,14 +40,14 @@ class DisplayWindowListenerController {
}
int[] registerListener(IDisplayWindowListener listener) {
+ mDisplayListeners.register(listener);
+ final IntArray displayIds = new IntArray();
synchronized (mService.mGlobalLock) {
- mDisplayListeners.register(listener);
- final IntArray displayIds = new IntArray();
mService.mAtmService.mRootWindowContainer.forAllDisplays((displayContent) -> {
displayIds.add(displayContent.mDisplayId);
});
- return displayIds.toArray();
}
+ return displayIds.toArray();
}
void unregisterListener(IDisplayWindowListener listener) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
index afb18f5be669..5c9ba401bf88 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
@@ -32,7 +32,7 @@ internal class IgnoreableExpect : TestRule {
private var ignore = false
- override fun apply(base: Statement?, description: Description?): Statement {
+ override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
ignore = false
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 206c90d0481a..6dc7361e5366 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -47,7 +47,7 @@ class DisplayTopologyCoordinatorTest {
private val mockTopology = mock<DisplayTopology>()
private val mockTopologyCopy = mock<DisplayTopology>()
private val mockTopologyGraph = mock<DisplayTopologyGraph>()
- private val mockIsExtendedDisplayEnabled = mock<() -> Boolean>()
+ private val mockIsExtendedDisplayAllowed = mock<() -> Boolean>()
private val mockTopologySavedCallback = mock<() -> Unit>()
private val mockTopologyChangedCallback =
mock<(android.util.Pair<DisplayTopology, DisplayTopologyGraph>) -> Unit>()
@@ -73,10 +73,10 @@ class DisplayTopologyCoordinatorTest {
) =
mockTopologyStore
}
- whenever(mockIsExtendedDisplayEnabled()).thenReturn(true)
+ whenever(mockIsExtendedDisplayAllowed()).thenReturn(true)
whenever(mockTopology.copy()).thenReturn(mockTopologyCopy)
whenever(mockTopologyCopy.getGraph(any())).thenReturn(mockTopologyGraph)
- coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayEnabled,
+ coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayAllowed,
mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot(),
mockTopologySavedCallback)
}
@@ -195,7 +195,7 @@ class DisplayTopologyCoordinatorTest {
@Test
fun addDisplay_external_extendedDisplaysDisabled() {
- whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+ whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
for (displayInfo in displayInfos) {
coordinator.onDisplayAdded(displayInfo)
@@ -208,7 +208,7 @@ class DisplayTopologyCoordinatorTest {
@Test
fun addDisplay_overlay_extendedDisplaysDisabled() {
displayInfos[0].type = Display.TYPE_OVERLAY
- whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+ whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
for (displayInfo in displayInfos) {
coordinator.onDisplayAdded(displayInfo)
@@ -314,7 +314,7 @@ class DisplayTopologyCoordinatorTest {
@Test
fun updateDisplay_external_extendedDisplaysDisabled() {
- whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+ whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
for (displayInfo in displayInfos) {
coordinator.onDisplayChanged(displayInfo)
@@ -328,7 +328,7 @@ class DisplayTopologyCoordinatorTest {
@Test
fun updateDisplay_overlay_extendedDisplaysDisabled() {
displayInfos[0].type = Display.TYPE_OVERLAY
- whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+ whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
coordinator.onDisplayChanged(displayInfos[0])
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
index 8ce05e2fa115..c9f86b04be22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -23,7 +23,6 @@ import static android.hardware.display.DisplayManagerInternal.DisplayPowerReques
import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
import static android.view.Display.DEFAULT_DISPLAY_GROUP;
-import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED;
import static com.android.server.power.ScreenUndimDetector.KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
import static com.android.server.power.ScreenUndimDetector.KEY_UNDIMS_REQUIRED;
@@ -49,6 +48,7 @@ import org.junit.runners.JUnit4;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* Tests for {@link com.android.server.power.ScreenUndimDetector}
@@ -61,7 +61,8 @@ public class ScreenUndimDetectorTest {
POLICY_DIM,
POLICY_BRIGHT);
private static final int OTHER_DISPLAY_GROUP = DEFAULT_DISPLAY_GROUP + 1;
-
+ private static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS =
+ TimeUnit.MINUTES.toMillis(5);
@ClassRule
public static final TestableContext sContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@@ -88,7 +89,8 @@ public class ScreenUndimDetectorTest {
@Before
public void setup() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_KEEP_SCREEN_ON_ENABLED, Boolean.TRUE.toString(), false /*makeDefault*/);
DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_UNDIMS_REQUIRED,
Integer.toString(1), false /*makeDefault*/);
@@ -108,10 +110,10 @@ public class ScreenUndimDetectorTest {
@Test
public void recordScreenPolicy_disabledByFlag_noop() {
+ setup();
DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/);
mScreenUndimDetector.readValuesFromDeviceConfig();
-
mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 5165e34c7fcd..fc864dd230d9 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
@@ -330,31 +331,24 @@ public class BatteryStatsHistoryTest {
return invocation.callRealMethod();
}).when(mHistory).readFragmentToParcel(any(), any());
- // Prepare history for iteration
- mHistory.iterate(0, MonotonicClock.UNDEFINED);
-
- Parcel parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
- assertThat(parcel).isNotNull();
- assertThat(mReadFiles).containsExactly("123.bh");
-
- // Skip to the end to force reading the next parcel
- parcel.setDataPosition(parcel.dataSize());
- mReadFiles.clear();
- parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
- assertThat(parcel).isNotNull();
- assertThat(mReadFiles).containsExactly("1000.bh");
-
- parcel.setDataPosition(parcel.dataSize());
- mReadFiles.clear();
- parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
- assertThat(parcel).isNotNull();
- assertThat(mReadFiles).containsExactly("2000.bh");
+ int eventsRead = 0;
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
+ while (iterator.hasNext()) {
+ HistoryItem item = iterator.next();
+ if (item.eventCode == HistoryItem.EVENT_JOB_START) {
+ eventsRead++;
+ assertThat(mReadFiles).containsExactly("123.bh");
+ } else if (item.eventCode == HistoryItem.EVENT_JOB_FINISH) {
+ eventsRead++;
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
+ } else if (item.eventCode == HistoryItem.EVENT_ALARM) {
+ eventsRead++;
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh");
+ }
+ }
- parcel.setDataPosition(parcel.dataSize());
- mReadFiles.clear();
- parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
- assertThat(parcel).isNull();
- assertThat(mReadFiles).isEmpty();
+ assertThat(eventsRead).isEqualTo(3);
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh", "3000.bh");
}
@Test
@@ -372,25 +366,19 @@ public class BatteryStatsHistoryTest {
return invocation.callRealMethod();
}).when(mHistory).readFragmentToParcel(any(), any());
- // Prepare history for iteration
- mHistory.iterate(1000, 3000);
-
- Parcel parcel = mHistory.getNextParcel(1000, 3000);
- assertThat(parcel).isNotNull();
- assertThat(mReadFiles).containsExactly("1000.bh");
-
- // Skip to the end to force reading the next parcel
- parcel.setDataPosition(parcel.dataSize());
- mReadFiles.clear();
- parcel = mHistory.getNextParcel(1000, 3000);
- assertThat(parcel).isNotNull();
- assertThat(mReadFiles).containsExactly("2000.bh");
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(1000, 3000);
+ while (iterator.hasNext()) {
+ HistoryItem item = iterator.next();
+ if (item.eventCode == HistoryItem.EVENT_JOB_START) {
+ fail("Event outside the range");
+ } else if (item.eventCode == HistoryItem.EVENT_JOB_FINISH) {
+ assertThat(mReadFiles).containsExactly("1000.bh");
+ } else if (item.eventCode == HistoryItem.EVENT_ALARM) {
+ fail("Event outside the range");
+ }
+ }
- parcel.setDataPosition(parcel.dataSize());
- mReadFiles.clear();
- parcel = mHistory.getNextParcel(1000, 3000);
- assertThat(parcel).isNull();
- assertThat(mReadFiles).isEmpty();
+ assertThat(mReadFiles).containsExactly("1000.bh", "2000.bh");
}
private void prepareMultiFileHistory() {
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
index ae973be17904..56f802b278c6 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
@@ -89,8 +89,7 @@ public class DiscreteAppOpXmlPersistenceTest {
int attributionChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
- uidState, accessTime, duration, attributionFlags, attributionChainId,
- DiscreteOpsXmlRegistry.ACCESS_TYPE_FINISH_OP);
+ uidState, accessTime, duration, attributionFlags, attributionChainId);
// Verify in-memory object is correct
fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
@@ -121,8 +120,7 @@ public class DiscreteAppOpXmlPersistenceTest {
int attributionChainId = 10;
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
- uidState, accessTime, duration, attributionFlags, attributionChainId,
- DiscreteOpsXmlRegistry.ACCESS_TYPE_START_OP);
+ uidState, accessTime, duration, attributionFlags, attributionChainId);
fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
duration, uidState, opFlags, attributionFlags, attributionChainId);
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
index 8eea1c73d4f2..6c66f149baa7 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
@@ -70,7 +70,7 @@ public class DiscreteOpsMigrationAndRollbackTest {
opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
opEvent.getDuration(), opEvent.getAttributionFlags(),
- (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+ (int) opEvent.getChainId());
}
xmlRegistry.writeAndClearOldAccessHistory();
assertThat(xmlRegistry.readLargestChainIdFromDiskLocked()).isEqualTo(RECORD_COUNT);
@@ -104,7 +104,7 @@ public class DiscreteOpsMigrationAndRollbackTest {
opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
opEvent.getDuration(), opEvent.getAttributionFlags(),
- (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+ (int) opEvent.getChainId());
}
// flush records from cache to the database.
sqlRegistry.shutdown();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 948371f74a9c..ad706e879b72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -63,11 +63,23 @@ public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBa
super(0.8f /* highResScale */, 0.5f /* lowResScale */);
}
+ private class TestActivitySnapshotController extends ActivitySnapshotController {
+ TestActivitySnapshotController(WindowManagerService service,
+ SnapshotPersistQueue persistQueue) {
+ super(service, persistQueue);
+ }
+ @Override
+ BaseAppSnapshotPersister.PersistInfoProvider createPersistInfoProvider(
+ WindowManagerService service) {
+ return mPersister.mPersistInfoProvider;
+ }
+ }
@Override
@Before
public void setUp() {
super.setUp();
- mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue);
+ mActivitySnapshotController = new TestActivitySnapshotController(
+ mWm, mSnapshotPersistQueue);
spyOn(mActivitySnapshotController);
doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
mActivitySnapshotController.resetTmpFields();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 51ea498811fc..f22ecb5eb3f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -68,11 +68,8 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
public void testPersistAndLoadSnapshot() {
mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- final File[] files = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+ final File[] files = convertFilePath("1.proto", "1.jpg");
+ final File[] nonExistsFiles = convertFilePath("1_reduced.proto");
assertTrueForFiles(files, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
@@ -92,14 +89,9 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
taskIds.add(1);
mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
mSnapshotPersistQueue.waitForQueueEmpty();
- final File[] existsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+ final File[] existsFiles = convertFilePath("1.proto", "1.jpg");
+ final File[] nonExistsFiles = convertFilePath("1_reduced.proto", "2.proto", "2.jpg",
+ "2_reduced.jpg");
assertTrueForFiles(existsFiles, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
}
@@ -112,14 +104,8 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- final File[] existsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/2.jpg")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+ final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "2.proto", "2.jpg");
+ final File[] nonExistsFiles = convertFilePath("1_reduced.jpg", "2_reduced.jpg");
assertTrueForFiles(existsFiles, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 4b54e4464ca7..af06c14516a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -75,9 +75,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
public void testPersistAndLoadSnapshot() {
mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+ final File[] files = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg");
assertTrueForFiles(files, File::exists, " must exist");
final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
assertNotNull(snapshot);
@@ -140,13 +138,8 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
mSnapshotPersistQueue.waitForQueueEmpty();
// Make sure 1,2 were purged but removeObsoleteFiles wasn't.
- final File[] existsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/3.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/4.proto")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/100.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.proto")};
+ final File[] existsFiles = convertFilePath("3.proto", "4.proto");
+ final File[] nonExistsFiles = convertFilePath("100.proto", "1.proto", "2.proto");
assertTrueForFiles(existsFiles, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
}
@@ -427,14 +420,8 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
taskIds.add(1);
mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
mSnapshotPersistQueue.waitForQueueEmpty();
- final File[] existsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+ final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg");
+ final File[] nonExistsFiles = convertFilePath("2.proto", "2.jpg", "2_reduced.jpg");
assertTrueForFiles(existsFiles, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
}
@@ -447,13 +434,8 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
mSnapshotPersistQueue.waitForQueueEmpty();
- final File[] existsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
- new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+ final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg", "2.proto",
+ "2.jpg", "2_reduced.jpg");
assertTrueForFiles(existsFiles, File::exists, " must exist");
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 1e16c97de647..b2c195e8ebaa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.ContextWrapper;
import android.content.res.Resources;
@@ -46,6 +47,7 @@ import android.window.TaskSnapshot;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.AfterClass;
@@ -129,12 +131,33 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
return;
}
for (File file : files) {
- if (!file.isDirectory()) {
- file.delete();
+ if (file.isDirectory()) {
+ final File[] subFiles = file.listFiles();
+ if (subFiles == null) {
+ continue;
+ }
+ for (File subFile : subFiles) {
+ subFile.delete();
+ }
}
+ file.delete();
}
}
+ File[] convertFilePath(@NonNull String... fileNames) {
+ final File[] files = new File[fileNames.length];
+ final String path;
+ if (Flags.scrambleSnapshotFileName()) {
+ path = mPersister.mPersistInfoProvider.getDirectory(mTestUserId).getPath();
+ } else {
+ path = FILES_DIR.getPath() + "/snapshots/";
+ }
+ for (int i = 0; i < fileNames.length; i++) {
+ files[i] = new File(path + fileNames[i]);
+ }
+ return files;
+ }
+
TaskSnapshot createSnapshot() {
return new TaskSnapshotBuilder().setTopActivityComponent(getUniqueComponentName()).build();
}
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
index 4995eebdd79e..fe72dae9ff34 100644
--- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -155,35 +155,35 @@ class IntDefProcessor : AbstractProcessor() {
) {
val indent = " "
- writer.appendln("{")
+ writer.appendLine("{")
val intDefTypesCount = annotationTypeToIntDefMapping.size
var currentIntDefTypesCount = 0
for ((field, intDefMapping) in annotationTypeToIntDefMapping) {
- writer.appendln("""$indent"$field": {""")
+ writer.appendLine("""$indent"$field": {""")
// Start IntDef
- writer.appendln("""$indent$indent"flag": ${intDefMapping.flag},""")
+ writer.appendLine("""$indent$indent"flag": ${intDefMapping.flag},""")
- writer.appendln("""$indent$indent"values": {""")
+ writer.appendLine("""$indent$indent"values": {""")
intDefMapping.entries.joinTo(writer, separator = ",\n") { (value, identifier) ->
"""$indent$indent$indent"$value": "$identifier""""
}
- writer.appendln()
- writer.appendln("$indent$indent}")
+ writer.appendLine()
+ writer.appendLine("$indent$indent}")
// End IntDef
writer.append("$indent}")
if (++currentIntDefTypesCount < intDefTypesCount) {
- writer.appendln(",")
+ writer.appendLine(",")
} else {
- writer.appendln("")
+ writer.appendLine("")
}
}
- writer.appendln("}")
+ writer.appendLine("}")
}
}
}