summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp11
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig1
-rw-r--r--core/java/android/os/CpuHeadroomParamsInternal.aidl1
-rw-r--r--core/java/android/os/Parcel.java233
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java45
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/com_android_internal_util_ArrayUtils.cpp122
-rw-r--r--core/res/Android.bp1
-rw-r--r--core/res/AndroidManifest.xml15
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java47
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--graphics/java/android/graphics/Path.java3
-rw-r--r--native/android/display_luts.cpp9
-rw-r--r--native/android/surface_control.cpp2
-rw-r--r--nfc/tests/src/android/nfc/NdefRecordTest.java18
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig19
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt136
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt5
-rw-r--r--packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt375
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt119
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt14
-rw-r--r--packages/SystemUI/lint-baseline.xml968
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt327
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt241
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt)80
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt25
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java310
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt283
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt208
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json95
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto5
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java411
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt33
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java27
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java29
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java363
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java74
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageVerificationState.java12
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java98
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java138
-rw-r--r--services/core/java/com/android/server/vibrator/VendorVibrationSession.java5
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java43
-rw-r--r--services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java295
113 files changed, 4613 insertions, 1869 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 3c2ae5a03004..371177c8b0a5 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1220,6 +1220,17 @@ java_aconfig_library {
}
java_aconfig_library {
+ name: "device_policy_aconfig_flags_java_export",
+ aconfig_declarations: "device_policy_aconfig_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
+
+java_aconfig_library {
name: "device_policy_aconfig_flags_lib_host",
aconfig_declarations: "device_policy_aconfig_flags",
host_supported: true,
diff --git a/core/api/current.txt b/core/api/current.txt
index 1b429776244c..523ce8e87ee8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17082,7 +17082,7 @@ package android.graphics {
method public void arcTo(@NonNull android.graphics.RectF, float, float);
method public void arcTo(float, float, float, float, float, float, boolean);
method public void close();
- method @Deprecated public void computeBounds(@NonNull android.graphics.RectF, boolean);
+ method public void computeBounds(@NonNull android.graphics.RectF, boolean);
method @FlaggedApi("com.android.graphics.flags.exact_compute_bounds") public void computeBounds(@NonNull android.graphics.RectF);
method public void conicTo(float, float, float, float, float);
method public void cubicTo(float, float, float, float, float, float);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a3ecef8ecad..50d2bfea87dd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -78,6 +78,7 @@ package android {
field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String BIND_ON_DEVICE_INTELLIGENCE_SERVICE = "android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE";
field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE = "android.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE";
field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
+ field @FlaggedApi("android.location.flags.population_density_provider") public static final String BIND_POPULATION_DENSITY_PROVIDER_SERVICE = "android.permission.BIND_POPULATION_DENSITY_PROVIDER_SERVICE";
field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final String BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE = "android.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE";
field public static final String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 22bc356899f4..361ba73817c7 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -398,6 +398,7 @@ flag {
flag {
name: "split_create_managed_profile_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Split up existing create and provision managed profile API."
bug: "375382324"
diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl
index d572f965579b..12b209357d9c 100644
--- a/core/java/android/os/CpuHeadroomParamsInternal.aidl
+++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl
@@ -28,6 +28,5 @@ parcelable CpuHeadroomParamsInternal {
int[] tids;
int calculationWindowMillis = 1000;
CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN;
- CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL;
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index bf7116d6a05b..6855d95d718f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -50,6 +50,7 @@ import com.android.internal.util.ArrayUtils;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import dalvik.annotation.optimization.NeverInline;
import libcore.util.SneakyThrow;
@@ -587,6 +588,17 @@ public final class Parcel {
return parcel;
}
+ @NeverInline
+ private void errorUsedWhileRecycling() {
+ Log.wtf(TAG, "Parcel used while recycled. "
+ + Log.getStackTraceString(new Throwable())
+ + " Original recycle call (if DEBUG_RECYCLE): ", mStack);
+ }
+
+ private void assertNotRecycled() {
+ if (mRecycled) errorUsedWhileRecycling();
+ }
+
/**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
@@ -635,6 +647,7 @@ public final class Parcel {
* @hide
*/
public void setReadWriteHelper(@Nullable ReadWriteHelper helper) {
+ assertNotRecycled();
mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
}
@@ -644,6 +657,7 @@ public final class Parcel {
* @hide
*/
public boolean hasReadWriteHelper() {
+ assertNotRecycled();
return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
}
@@ -670,6 +684,7 @@ public final class Parcel {
* @hide
*/
public final void markSensitive() {
+ assertNotRecycled();
nativeMarkSensitive(mNativePtr);
}
@@ -686,6 +701,7 @@ public final class Parcel {
* @hide
*/
public final boolean isForRpc() {
+ assertNotRecycled();
return nativeIsForRpc(mNativePtr);
}
@@ -693,21 +709,25 @@ public final class Parcel {
@ParcelFlags
@TestApi
public int getFlags() {
+ assertNotRecycled();
return mFlags;
}
/** @hide */
public void setFlags(@ParcelFlags int flags) {
+ assertNotRecycled();
mFlags = flags;
}
/** @hide */
public void addFlags(@ParcelFlags int flags) {
+ assertNotRecycled();
mFlags |= flags;
}
/** @hide */
private boolean hasFlags(@ParcelFlags int flags) {
+ assertNotRecycled();
return (mFlags & flags) == flags;
}
@@ -720,6 +740,7 @@ public final class Parcel {
// We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
// This would only work when used on a reply parcel by a binder object that's allowed-blocking.
public void setPropagateAllowBlocking() {
+ assertNotRecycled();
addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
}
@@ -727,6 +748,7 @@ public final class Parcel {
* Returns the total amount of data contained in the parcel.
*/
public int dataSize() {
+ assertNotRecycled();
return nativeDataSize(mNativePtr);
}
@@ -735,6 +757,7 @@ public final class Parcel {
* parcel. That is, {@link #dataSize}-{@link #dataPosition}.
*/
public final int dataAvail() {
+ assertNotRecycled();
return nativeDataAvail(mNativePtr);
}
@@ -743,6 +766,7 @@ public final class Parcel {
* more than {@link #dataSize}.
*/
public final int dataPosition() {
+ assertNotRecycled();
return nativeDataPosition(mNativePtr);
}
@@ -753,6 +777,7 @@ public final class Parcel {
* data buffer.
*/
public final int dataCapacity() {
+ assertNotRecycled();
return nativeDataCapacity(mNativePtr);
}
@@ -764,6 +789,7 @@ public final class Parcel {
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
+ assertNotRecycled();
nativeSetDataSize(mNativePtr, size);
}
@@ -773,6 +799,7 @@ public final class Parcel {
* {@link #dataSize}.
*/
public final void setDataPosition(int pos) {
+ assertNotRecycled();
nativeSetDataPosition(mNativePtr, pos);
}
@@ -784,11 +811,13 @@ public final class Parcel {
* with this method.
*/
public final void setDataCapacity(int size) {
+ assertNotRecycled();
nativeSetDataCapacity(mNativePtr, size);
}
/** @hide */
public final boolean pushAllowFds(boolean allowFds) {
+ assertNotRecycled();
return nativePushAllowFds(mNativePtr, allowFds);
}
@@ -809,6 +838,7 @@ public final class Parcel {
* in different versions of the platform.
*/
public final byte[] marshall() {
+ assertNotRecycled();
return nativeMarshall(mNativePtr);
}
@@ -816,15 +846,18 @@ public final class Parcel {
* Fills the raw bytes of this Parcel with the supplied data.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
+ assertNotRecycled();
nativeUnmarshall(mNativePtr, data, offset, length);
}
public final void appendFrom(Parcel parcel, int offset, int length) {
+ assertNotRecycled();
nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
/** @hide */
public int compareData(Parcel other) {
+ assertNotRecycled();
return nativeCompareData(mNativePtr, other.mNativePtr);
}
@@ -835,6 +868,7 @@ public final class Parcel {
/** @hide */
public final void setClassCookie(Class clz, Object cookie) {
+ assertNotRecycled();
if (mClassCookies == null) {
mClassCookies = new ArrayMap<>();
}
@@ -844,11 +878,13 @@ public final class Parcel {
/** @hide */
@Nullable
public final Object getClassCookie(Class clz) {
+ assertNotRecycled();
return mClassCookies != null ? mClassCookies.get(clz) : null;
}
/** @hide */
public void removeClassCookie(Class clz, Object expectedCookie) {
+ assertNotRecycled();
if (mClassCookies != null) {
Object removedCookie = mClassCookies.remove(clz);
if (removedCookie != expectedCookie) {
@@ -866,21 +902,25 @@ public final class Parcel {
* @hide
*/
public boolean hasClassCookie(Class clz) {
+ assertNotRecycled();
return mClassCookies != null && mClassCookies.containsKey(clz);
}
/** @hide */
public final void adoptClassCookies(Parcel from) {
+ assertNotRecycled();
mClassCookies = from.mClassCookies;
}
/** @hide */
public Map<Class, Object> copyClassCookies() {
+ assertNotRecycled();
return new ArrayMap<>(mClassCookies);
}
/** @hide */
public void putClassCookies(Map<Class, Object> cookies) {
+ assertNotRecycled();
if (cookies == null) {
return;
}
@@ -894,6 +934,7 @@ public final class Parcel {
* Report whether the parcel contains any marshalled file descriptors.
*/
public boolean hasFileDescriptors() {
+ assertNotRecycled();
return nativeHasFileDescriptors(mNativePtr);
}
@@ -909,6 +950,7 @@ public final class Parcel {
* @throws IllegalArgumentException if the parameters are out of the permitted ranges.
*/
public boolean hasFileDescriptors(int offset, int length) {
+ assertNotRecycled();
return nativeHasFileDescriptorsInRange(mNativePtr, offset, length);
}
@@ -993,6 +1035,7 @@ public final class Parcel {
* @hide
*/
public boolean hasBinders() {
+ assertNotRecycled();
return nativeHasBinders(mNativePtr);
}
@@ -1010,6 +1053,7 @@ public final class Parcel {
* @hide
*/
public boolean hasBinders(int offset, int length) {
+ assertNotRecycled();
return nativeHasBindersInRange(mNativePtr, offset, length);
}
@@ -1020,6 +1064,7 @@ public final class Parcel {
* at the beginning of transactions as a header.
*/
public final void writeInterfaceToken(@NonNull String interfaceName) {
+ assertNotRecycled();
nativeWriteInterfaceToken(mNativePtr, interfaceName);
}
@@ -1030,6 +1075,7 @@ public final class Parcel {
* should propagate to the caller.
*/
public final void enforceInterface(@NonNull String interfaceName) {
+ assertNotRecycled();
nativeEnforceInterface(mNativePtr, interfaceName);
}
@@ -1040,6 +1086,7 @@ public final class Parcel {
* When used over binder, this exception should propagate to the caller.
*/
public void enforceNoDataAvail() {
+ assertNotRecycled();
final int n = dataAvail();
if (n > 0) {
throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n);
@@ -1056,6 +1103,7 @@ public final class Parcel {
* @hide
*/
public boolean replaceCallingWorkSourceUid(int workSourceUid) {
+ assertNotRecycled();
return nativeReplaceCallingWorkSourceUid(mNativePtr, workSourceUid);
}
@@ -1072,6 +1120,7 @@ public final class Parcel {
* @hide
*/
public int readCallingWorkSourceUid() {
+ assertNotRecycled();
return nativeReadCallingWorkSourceUid(mNativePtr);
}
@@ -1081,6 +1130,7 @@ public final class Parcel {
* @param b Bytes to place into the parcel.
*/
public final void writeByteArray(@Nullable byte[] b) {
+ assertNotRecycled();
writeByteArray(b, 0, (b != null) ? b.length : 0);
}
@@ -1092,6 +1142,7 @@ public final class Parcel {
* @param len Number of bytes to write.
*/
public final void writeByteArray(@Nullable byte[] b, int offset, int len) {
+ assertNotRecycled();
if (b == null) {
writeInt(-1);
return;
@@ -1113,6 +1164,7 @@ public final class Parcel {
* @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b) {
+ assertNotRecycled();
writeBlob(b, 0, (b != null) ? b.length : 0);
}
@@ -1131,6 +1183,7 @@ public final class Parcel {
* @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b, int offset, int len) {
+ assertNotRecycled();
if (b == null) {
writeInt(-1);
return;
@@ -1149,6 +1202,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
+ assertNotRecycled();
int err = nativeWriteInt(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1160,6 +1214,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeLong(long val) {
+ assertNotRecycled();
int err = nativeWriteLong(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1171,6 +1226,7 @@ public final class Parcel {
* dataPosition(), growing dataCapacity() if needed.
*/
public final void writeFloat(float val) {
+ assertNotRecycled();
int err = nativeWriteFloat(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1182,6 +1238,7 @@ public final class Parcel {
* current dataPosition(), growing dataCapacity() if needed.
*/
public final void writeDouble(double val) {
+ assertNotRecycled();
int err = nativeWriteDouble(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1193,16 +1250,19 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeString(@Nullable String val) {
+ assertNotRecycled();
writeString16(val);
}
/** {@hide} */
public final void writeString8(@Nullable String val) {
+ assertNotRecycled();
mReadWriteHelper.writeString8(this, val);
}
/** {@hide} */
public final void writeString16(@Nullable String val) {
+ assertNotRecycled();
mReadWriteHelper.writeString16(this, val);
}
@@ -1214,16 +1274,19 @@ public final class Parcel {
* @hide
*/
public void writeStringNoHelper(@Nullable String val) {
+ assertNotRecycled();
writeString16NoHelper(val);
}
/** {@hide} */
public void writeString8NoHelper(@Nullable String val) {
+ assertNotRecycled();
nativeWriteString8(mNativePtr, val);
}
/** {@hide} */
public void writeString16NoHelper(@Nullable String val) {
+ assertNotRecycled();
nativeWriteString16(mNativePtr, val);
}
@@ -1235,6 +1298,7 @@ public final class Parcel {
* for true or false, respectively, but may change in the future.
*/
public final void writeBoolean(boolean val) {
+ assertNotRecycled();
writeInt(val ? 1 : 0);
}
@@ -1246,6 +1310,7 @@ public final class Parcel {
@UnsupportedAppUsage
@RavenwoodThrow(blockedBy = android.text.Spanned.class)
public final void writeCharSequence(@Nullable CharSequence val) {
+ assertNotRecycled();
TextUtils.writeToParcel(val, this, 0);
}
@@ -1254,6 +1319,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeStrongBinder(IBinder val) {
+ assertNotRecycled();
nativeWriteStrongBinder(mNativePtr, val);
}
@@ -1262,6 +1328,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeStrongInterface(IInterface val) {
+ assertNotRecycled();
writeStrongBinder(val == null ? null : val.asBinder());
}
@@ -1276,6 +1343,7 @@ public final class Parcel {
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(@NonNull FileDescriptor val) {
+ assertNotRecycled();
nativeWriteFileDescriptor(mNativePtr, val);
}
@@ -1284,6 +1352,7 @@ public final class Parcel {
* This will be the new name for writeFileDescriptor, for consistency.
**/
public final void writeRawFileDescriptor(@NonNull FileDescriptor val) {
+ assertNotRecycled();
nativeWriteFileDescriptor(mNativePtr, val);
}
@@ -1294,6 +1363,7 @@ public final class Parcel {
* @param value The array of objects to be written.
*/
public final void writeRawFileDescriptorArray(@Nullable FileDescriptor[] value) {
+ assertNotRecycled();
if (value != null) {
int N = value.length;
writeInt(N);
@@ -1313,6 +1383,7 @@ public final class Parcel {
* the future.
*/
public final void writeByte(byte val) {
+ assertNotRecycled();
writeInt(val);
}
@@ -1328,6 +1399,7 @@ public final class Parcel {
* allows you to avoid mysterious type errors at the point of marshalling.
*/
public final void writeMap(@Nullable Map val) {
+ assertNotRecycled();
writeMapInternal((Map<String, Object>) val);
}
@@ -1336,6 +1408,7 @@ public final class Parcel {
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeMapInternal(@Nullable Map<String,Object> val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1361,6 +1434,7 @@ public final class Parcel {
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1390,6 +1464,7 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void writeArrayMap(@Nullable ArrayMap<String, Object> val) {
+ assertNotRecycled();
writeArrayMapInternal(val);
}
@@ -1408,6 +1483,7 @@ public final class Parcel {
*/
public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val,
int parcelableFlags) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1429,6 +1505,7 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
+ assertNotRecycled();
final int size = (val != null) ? val.size() : -1;
writeInt(size);
for (int i = 0; i < size; i++) {
@@ -1441,6 +1518,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeBundle(@Nullable Bundle val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1454,6 +1532,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writePersistableBundle(@Nullable PersistableBundle val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1467,6 +1546,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeSize(@NonNull Size val) {
+ assertNotRecycled();
writeInt(val.getWidth());
writeInt(val.getHeight());
}
@@ -1476,6 +1556,7 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeSizeF(@NonNull SizeF val) {
+ assertNotRecycled();
writeFloat(val.getWidth());
writeFloat(val.getHeight());
}
@@ -1486,6 +1567,7 @@ public final class Parcel {
* {@link #writeValue} and must follow the specification there.
*/
public final void writeList(@Nullable List val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1505,6 +1587,7 @@ public final class Parcel {
* {@link #writeValue} and must follow the specification there.
*/
public final void writeArray(@Nullable Object[] val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1525,6 +1608,7 @@ public final class Parcel {
* specification there.
*/
public final <T> void writeSparseArray(@Nullable SparseArray<T> val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1540,6 +1624,7 @@ public final class Parcel {
}
public final void writeSparseBooleanArray(@Nullable SparseBooleanArray val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1558,6 +1643,7 @@ public final class Parcel {
* @hide
*/
public final void writeSparseIntArray(@Nullable SparseIntArray val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1573,6 +1659,7 @@ public final class Parcel {
}
public final void writeBooleanArray(@Nullable boolean[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1607,6 +1694,7 @@ public final class Parcel {
}
private void ensureWithinMemoryLimit(int typeSize, @NonNull int... dimensions) {
+ assertNotRecycled();
// For Multidimensional arrays, Calculate total object
// which will be allocated.
int totalObjects = 1;
@@ -1624,6 +1712,7 @@ public final class Parcel {
}
private void ensureWithinMemoryLimit(int typeSize, int length) {
+ assertNotRecycled();
int estimatedAllocationSize = 0;
try {
estimatedAllocationSize = Math.multiplyExact(typeSize, length);
@@ -1647,6 +1736,7 @@ public final class Parcel {
@Nullable
public final boolean[] createBooleanArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_BOOLEAN, N);
// >>2 as a fast divide-by-4 works in the create*Array() functions
@@ -1664,6 +1754,7 @@ public final class Parcel {
}
public final void readBooleanArray(@NonNull boolean[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1676,6 +1767,7 @@ public final class Parcel {
/** @hide */
public void writeShortArray(@Nullable short[] val) {
+ assertNotRecycled();
if (val != null) {
int n = val.length;
writeInt(n);
@@ -1690,6 +1782,7 @@ public final class Parcel {
/** @hide */
@Nullable
public short[] createShortArray() {
+ assertNotRecycled();
int n = readInt();
ensureWithinMemoryLimit(SIZE_SHORT, n);
if (n >= 0 && n <= (dataAvail() >> 2)) {
@@ -1705,6 +1798,7 @@ public final class Parcel {
/** @hide */
public void readShortArray(@NonNull short[] val) {
+ assertNotRecycled();
int n = readInt();
if (n == val.length) {
for (int i = 0; i < n; i++) {
@@ -1716,6 +1810,7 @@ public final class Parcel {
}
public final void writeCharArray(@Nullable char[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1729,6 +1824,7 @@ public final class Parcel {
@Nullable
public final char[] createCharArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_CHAR, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1743,6 +1839,7 @@ public final class Parcel {
}
public final void readCharArray(@NonNull char[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1754,6 +1851,7 @@ public final class Parcel {
}
public final void writeIntArray(@Nullable int[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1767,6 +1865,7 @@ public final class Parcel {
@Nullable
public final int[] createIntArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_INT, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1781,6 +1880,7 @@ public final class Parcel {
}
public final void readIntArray(@NonNull int[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1792,6 +1892,7 @@ public final class Parcel {
}
public final void writeLongArray(@Nullable long[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1805,6 +1906,7 @@ public final class Parcel {
@Nullable
public final long[] createLongArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_LONG, N);
// >>3 because stored longs are 64 bits
@@ -1820,6 +1922,7 @@ public final class Parcel {
}
public final void readLongArray(@NonNull long[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1831,6 +1934,7 @@ public final class Parcel {
}
public final void writeFloatArray(@Nullable float[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1844,6 +1948,7 @@ public final class Parcel {
@Nullable
public final float[] createFloatArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_FLOAT, N);
// >>2 because stored floats are 4 bytes
@@ -1859,6 +1964,7 @@ public final class Parcel {
}
public final void readFloatArray(@NonNull float[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1870,6 +1976,7 @@ public final class Parcel {
}
public final void writeDoubleArray(@Nullable double[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1883,6 +1990,7 @@ public final class Parcel {
@Nullable
public final double[] createDoubleArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_DOUBLE, N);
// >>3 because stored doubles are 8 bytes
@@ -1898,6 +2006,7 @@ public final class Parcel {
}
public final void readDoubleArray(@NonNull double[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1909,20 +2018,24 @@ public final class Parcel {
}
public final void writeStringArray(@Nullable String[] val) {
+ assertNotRecycled();
writeString16Array(val);
}
@Nullable
public final String[] createStringArray() {
+ assertNotRecycled();
return createString16Array();
}
public final void readStringArray(@NonNull String[] val) {
+ assertNotRecycled();
readString16Array(val);
}
/** {@hide} */
public final void writeString8Array(@Nullable String[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1937,6 +2050,7 @@ public final class Parcel {
/** {@hide} */
@Nullable
public final String[] createString8Array() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -1952,6 +2066,7 @@ public final class Parcel {
/** {@hide} */
public final void readString8Array(@NonNull String[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1964,6 +2079,7 @@ public final class Parcel {
/** {@hide} */
public final void writeString16Array(@Nullable String[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1978,6 +2094,7 @@ public final class Parcel {
/** {@hide} */
@Nullable
public final String[] createString16Array() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -1993,6 +2110,7 @@ public final class Parcel {
/** {@hide} */
public final void readString16Array(@NonNull String[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2004,6 +2122,7 @@ public final class Parcel {
}
public final void writeBinderArray(@Nullable IBinder[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2028,6 +2147,7 @@ public final class Parcel {
*/
public final <T extends IInterface> void writeInterfaceArray(
@SuppressLint("ArrayReturn") @Nullable T[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2043,6 +2163,7 @@ public final class Parcel {
* @hide
*/
public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2058,6 +2179,7 @@ public final class Parcel {
* @hide
*/
public final void writeCharSequenceList(@Nullable ArrayList<CharSequence> val) {
+ assertNotRecycled();
if (val != null) {
int N = val.size();
writeInt(N);
@@ -2071,6 +2193,7 @@ public final class Parcel {
@Nullable
public final IBinder[] createBinderArray() {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2085,6 +2208,7 @@ public final class Parcel {
}
public final void readBinderArray(@NonNull IBinder[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2106,6 +2230,7 @@ public final class Parcel {
@Nullable
public final <T extends IInterface> T[] createInterfaceArray(
@NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
+ assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2130,6 +2255,7 @@ public final class Parcel {
public final <T extends IInterface> void readInterfaceArray(
@SuppressLint("ArrayReturn") @NonNull T[] val,
@NonNull Function<IBinder, T> asInterface) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2155,6 +2281,7 @@ public final class Parcel {
* @see Parcelable
*/
public final <T extends Parcelable> void writeTypedList(@Nullable List<T> val) {
+ assertNotRecycled();
writeTypedList(val, 0);
}
@@ -2174,6 +2301,7 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val,
int parcelableFlags) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2203,6 +2331,7 @@ public final class Parcel {
* @see Parcelable
*/
public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2228,6 +2357,7 @@ public final class Parcel {
* @see #readStringList
*/
public final void writeStringList(@Nullable List<String> val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2253,6 +2383,7 @@ public final class Parcel {
* @see #readBinderList
*/
public final void writeBinderList(@Nullable List<IBinder> val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2275,6 +2406,7 @@ public final class Parcel {
* @see #readInterfaceList
*/
public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2296,6 +2428,7 @@ public final class Parcel {
* @see #readParcelableList(List, ClassLoader)
*/
public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2330,6 +2463,7 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeTypedArray(@Nullable T[] val,
int parcelableFlags) {
+ assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2352,6 +2486,7 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeTypedObject(@Nullable T val,
int parcelableFlags) {
+ assertNotRecycled();
if (val != null) {
writeInt(1);
val.writeToParcel(this, parcelableFlags);
@@ -2389,6 +2524,7 @@ public final class Parcel {
*/
public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
@NonNull int... dimensions) {
+ assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2500,6 +2636,7 @@ public final class Parcel {
* should be used).</p>
*/
public final void writeValue(@Nullable Object v) {
+ assertNotRecycled();
if (v instanceof LazyValue) {
LazyValue value = (LazyValue) v;
value.writeToParcel(this);
@@ -2617,6 +2754,7 @@ public final class Parcel {
* @hide
*/
public void writeValue(int type, @Nullable Object v) {
+ assertNotRecycled();
switch (type) {
case VAL_NULL:
break;
@@ -2730,6 +2868,7 @@ public final class Parcel {
* {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
*/
public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
+ assertNotRecycled();
if (p == null) {
writeString(null);
return;
@@ -2745,6 +2884,7 @@ public final class Parcel {
* @see #readParcelableCreator
*/
public final void writeParcelableCreator(@NonNull Parcelable p) {
+ assertNotRecycled();
String name = p.getClass().getName();
writeString(name);
}
@@ -2783,6 +2923,7 @@ public final class Parcel {
*/
@TestApi
public boolean allowSquashing() {
+ assertNotRecycled();
boolean previous = mAllowSquashing;
mAllowSquashing = true;
return previous;
@@ -2794,6 +2935,7 @@ public final class Parcel {
*/
@TestApi
public void restoreAllowSquashing(boolean previous) {
+ assertNotRecycled();
mAllowSquashing = previous;
if (!mAllowSquashing) {
mWrittenSquashableParcelables = null;
@@ -2850,6 +2992,7 @@ public final class Parcel {
* @hide
*/
public boolean maybeWriteSquashed(@NonNull Parcelable p) {
+ assertNotRecycled();
if (!mAllowSquashing) {
// Don't squash, and don't put it in the map either.
writeInt(0);
@@ -2900,6 +3043,7 @@ public final class Parcel {
@SuppressWarnings("unchecked")
@Nullable
public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
+ assertNotRecycled();
final int offset = readInt();
final int pos = dataPosition();
@@ -2933,6 +3077,7 @@ public final class Parcel {
* using the other approaches to writing data in to a Parcel.
*/
public final void writeSerializable(@Nullable Serializable s) {
+ assertNotRecycled();
if (s == null) {
writeString(null);
return;
@@ -2985,6 +3130,7 @@ public final class Parcel {
*/
@RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeException(@NonNull Exception e) {
+ assertNotRecycled();
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
int code = getExceptionCode(e);
@@ -3065,6 +3211,7 @@ public final class Parcel {
/** @hide */
public void writeStackTrace(@NonNull Throwable e) {
+ assertNotRecycled();
final int sizePosition = dataPosition();
writeInt(0); // Header size will be filled in later
StackTraceElement[] stackTrace = e.getStackTrace();
@@ -3090,6 +3237,7 @@ public final class Parcel {
*/
@RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeNoException() {
+ assertNotRecycled();
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
// Despite the name of this function ("write no exception"),
@@ -3133,6 +3281,7 @@ public final class Parcel {
* @see #writeNoException
*/
public final void readException() {
+ assertNotRecycled();
int code = readExceptionCode();
if (code != 0) {
String msg = readString();
@@ -3156,6 +3305,7 @@ public final class Parcel {
@UnsupportedAppUsage
@TestApi
public final int readExceptionCode() {
+ assertNotRecycled();
int code = readInt();
if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
AppOpsManager.readAndLogNotedAppops(this);
@@ -3189,6 +3339,7 @@ public final class Parcel {
* @param msg The exception message.
*/
public final void readException(int code, String msg) {
+ assertNotRecycled();
String remoteStackTrace = null;
final int remoteStackPayloadSize = readInt();
if (remoteStackPayloadSize > 0) {
@@ -3219,6 +3370,7 @@ public final class Parcel {
/** @hide */
public Exception createExceptionOrNull(int code, String msg) {
+ assertNotRecycled();
switch (code) {
case EX_PARCELABLE:
if (readInt() > 0) {
@@ -3251,6 +3403,7 @@ public final class Parcel {
* Read an integer value from the parcel at the current dataPosition().
*/
public final int readInt() {
+ assertNotRecycled();
return nativeReadInt(mNativePtr);
}
@@ -3258,6 +3411,7 @@ public final class Parcel {
* Read a long integer value from the parcel at the current dataPosition().
*/
public final long readLong() {
+ assertNotRecycled();
return nativeReadLong(mNativePtr);
}
@@ -3266,6 +3420,7 @@ public final class Parcel {
* dataPosition().
*/
public final float readFloat() {
+ assertNotRecycled();
return nativeReadFloat(mNativePtr);
}
@@ -3274,6 +3429,7 @@ public final class Parcel {
* current dataPosition().
*/
public final double readDouble() {
+ assertNotRecycled();
return nativeReadDouble(mNativePtr);
}
@@ -3282,16 +3438,19 @@ public final class Parcel {
*/
@Nullable
public final String readString() {
+ assertNotRecycled();
return readString16();
}
/** {@hide} */
public final @Nullable String readString8() {
+ assertNotRecycled();
return mReadWriteHelper.readString8(this);
}
/** {@hide} */
public final @Nullable String readString16() {
+ assertNotRecycled();
return mReadWriteHelper.readString16(this);
}
@@ -3303,16 +3462,19 @@ public final class Parcel {
* @hide
*/
public @Nullable String readStringNoHelper() {
+ assertNotRecycled();
return readString16NoHelper();
}
/** {@hide} */
public @Nullable String readString8NoHelper() {
+ assertNotRecycled();
return nativeReadString8(mNativePtr);
}
/** {@hide} */
public @Nullable String readString16NoHelper() {
+ assertNotRecycled();
return nativeReadString16(mNativePtr);
}
@@ -3320,6 +3482,7 @@ public final class Parcel {
* Read a boolean value from the parcel at the current dataPosition().
*/
public final boolean readBoolean() {
+ assertNotRecycled();
return readInt() != 0;
}
@@ -3330,6 +3493,7 @@ public final class Parcel {
@UnsupportedAppUsage
@Nullable
public final CharSequence readCharSequence() {
+ assertNotRecycled();
return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this);
}
@@ -3337,6 +3501,7 @@ public final class Parcel {
* Read an object from the parcel at the current dataPosition().
*/
public final IBinder readStrongBinder() {
+ assertNotRecycled();
final IBinder result = nativeReadStrongBinder(mNativePtr);
// If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
@@ -3352,6 +3517,7 @@ public final class Parcel {
* Read a FileDescriptor from the parcel at the current dataPosition().
*/
public final ParcelFileDescriptor readFileDescriptor() {
+ assertNotRecycled();
FileDescriptor fd = nativeReadFileDescriptor(mNativePtr);
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
@@ -3359,6 +3525,7 @@ public final class Parcel {
/** {@hide} */
@UnsupportedAppUsage
public final FileDescriptor readRawFileDescriptor() {
+ assertNotRecycled();
return nativeReadFileDescriptor(mNativePtr);
}
@@ -3369,6 +3536,7 @@ public final class Parcel {
**/
@Nullable
public final FileDescriptor[] createRawFileDescriptorArray() {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3388,6 +3556,7 @@ public final class Parcel {
* @return the FileDescriptor array, or null if the array is null.
**/
public final void readRawFileDescriptorArray(FileDescriptor[] val) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -3402,6 +3571,7 @@ public final class Parcel {
* Read a byte value from the parcel at the current dataPosition().
*/
public final byte readByte() {
+ assertNotRecycled();
return (byte)(readInt() & 0xff);
}
@@ -3416,6 +3586,7 @@ public final class Parcel {
*/
@Deprecated
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
+ assertNotRecycled();
readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
}
@@ -3429,6 +3600,7 @@ public final class Parcel {
public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
@Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
@NonNull Class<V> clazzValue) {
+ assertNotRecycled();
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
readMapInternal(outVal, loader, clazzKey, clazzValue);
@@ -3447,6 +3619,7 @@ public final class Parcel {
*/
@Deprecated
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
+ assertNotRecycled();
int N = readInt();
readListInternal(outVal, N, loader, /* clazz */ null);
}
@@ -3468,6 +3641,7 @@ public final class Parcel {
*/
public <T> void readList(@NonNull List<? super T> outVal,
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
int n = readInt();
readListInternal(outVal, n, loader, clazz);
@@ -3487,6 +3661,7 @@ public final class Parcel {
@Deprecated
@Nullable
public HashMap readHashMap(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
}
@@ -3501,6 +3676,7 @@ public final class Parcel {
@Nullable
public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+ assertNotRecycled();
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
return readHashMapInternal(loader, clazzKey, clazzValue);
@@ -3513,6 +3689,7 @@ public final class Parcel {
*/
@Nullable
public final Bundle readBundle() {
+ assertNotRecycled();
return readBundle(null);
}
@@ -3524,6 +3701,7 @@ public final class Parcel {
*/
@Nullable
public final Bundle readBundle(@Nullable ClassLoader loader) {
+ assertNotRecycled();
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3544,6 +3722,7 @@ public final class Parcel {
*/
@Nullable
public final PersistableBundle readPersistableBundle() {
+ assertNotRecycled();
return readPersistableBundle(null);
}
@@ -3555,6 +3734,7 @@ public final class Parcel {
*/
@Nullable
public final PersistableBundle readPersistableBundle(@Nullable ClassLoader loader) {
+ assertNotRecycled();
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3573,6 +3753,7 @@ public final class Parcel {
*/
@NonNull
public final Size readSize() {
+ assertNotRecycled();
final int width = readInt();
final int height = readInt();
return new Size(width, height);
@@ -3583,6 +3764,7 @@ public final class Parcel {
*/
@NonNull
public final SizeF readSizeF() {
+ assertNotRecycled();
final float width = readFloat();
final float height = readFloat();
return new SizeF(width, height);
@@ -3593,6 +3775,7 @@ public final class Parcel {
*/
@Nullable
public final byte[] createByteArray() {
+ assertNotRecycled();
return nativeCreateByteArray(mNativePtr);
}
@@ -3601,6 +3784,7 @@ public final class Parcel {
* given byte array.
*/
public final void readByteArray(@NonNull byte[] val) {
+ assertNotRecycled();
boolean valid = nativeReadByteArray(mNativePtr, val, (val != null) ? val.length : 0);
if (!valid) {
throw new RuntimeException("bad array lengths");
@@ -3613,6 +3797,7 @@ public final class Parcel {
*/
@Nullable
public final byte[] readBlob() {
+ assertNotRecycled();
return nativeReadBlob(mNativePtr);
}
@@ -3623,6 +3808,7 @@ public final class Parcel {
@UnsupportedAppUsage
@Nullable
public final String[] readStringArray() {
+ assertNotRecycled();
return createString16Array();
}
@@ -3632,6 +3818,7 @@ public final class Parcel {
*/
@Nullable
public final CharSequence[] readCharSequenceArray() {
+ assertNotRecycled();
CharSequence[] array = null;
int length = readInt();
@@ -3654,6 +3841,7 @@ public final class Parcel {
*/
@Nullable
public final ArrayList<CharSequence> readCharSequenceList() {
+ assertNotRecycled();
ArrayList<CharSequence> array = null;
int length = readInt();
@@ -3683,6 +3871,7 @@ public final class Parcel {
@Deprecated
@Nullable
public ArrayList readArrayList(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readArrayListInternal(loader, /* clazz */ null);
}
@@ -3705,6 +3894,7 @@ public final class Parcel {
@Nullable
public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
@NonNull Class<? extends T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
return readArrayListInternal(loader, clazz);
}
@@ -3724,6 +3914,7 @@ public final class Parcel {
@Deprecated
@Nullable
public Object[] readArray(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readArrayInternal(loader, /* clazz */ null);
}
@@ -3745,6 +3936,7 @@ public final class Parcel {
@SuppressLint({"ArrayReturn", "NullableCollection"})
@Nullable
public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
return readArrayInternal(loader, clazz);
}
@@ -3764,6 +3956,7 @@ public final class Parcel {
@Deprecated
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readSparseArrayInternal(loader, /* clazz */ null);
}
@@ -3785,6 +3978,7 @@ public final class Parcel {
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
@NonNull Class<? extends T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
return readSparseArrayInternal(loader, clazz);
}
@@ -3796,6 +3990,7 @@ public final class Parcel {
*/
@Nullable
public final SparseBooleanArray readSparseBooleanArray() {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3812,6 +4007,7 @@ public final class Parcel {
*/
@Nullable
public final SparseIntArray readSparseIntArray() {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3836,6 +4032,7 @@ public final class Parcel {
*/
@Nullable
public final <T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c) {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3859,6 +4056,7 @@ public final class Parcel {
* @see #writeTypedList
*/
public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
+ assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -3888,6 +4086,7 @@ public final class Parcel {
*/
public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray(
@NonNull Parcelable.Creator<T> creator) {
+ assertNotRecycled();
final int count = readInt();
if (count < 0) {
return null;
@@ -3917,6 +4116,7 @@ public final class Parcel {
*/
public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap(
@NonNull Parcelable.Creator<T> creator) {
+ assertNotRecycled();
final int count = readInt();
if (count < 0) {
return null;
@@ -3944,6 +4144,7 @@ public final class Parcel {
*/
@Nullable
public final ArrayList<String> createStringArrayList() {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3970,6 +4171,7 @@ public final class Parcel {
*/
@Nullable
public final ArrayList<IBinder> createBinderArrayList() {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3997,6 +4199,7 @@ public final class Parcel {
@Nullable
public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
@NonNull Function<IBinder, T> asInterface) {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4017,6 +4220,7 @@ public final class Parcel {
* @see #writeStringList
*/
public final void readStringList(@NonNull List<String> list) {
+ assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4038,6 +4242,7 @@ public final class Parcel {
* @see #writeBinderList
*/
public final void readBinderList(@NonNull List<IBinder> list) {
+ assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4060,6 +4265,7 @@ public final class Parcel {
*/
public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
@NonNull Function<IBinder, T> asInterface) {
+ assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4091,6 +4297,7 @@ public final class Parcel {
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl) {
+ assertNotRecycled();
return readParcelableListInternal(list, cl, /*clazz*/ null);
}
@@ -4112,6 +4319,7 @@ public final class Parcel {
@NonNull
public <T> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(list);
Objects.requireNonNull(clazz);
return readParcelableListInternal(list, cl, clazz);
@@ -4157,6 +4365,7 @@ public final class Parcel {
*/
@Nullable
public final <T> T[] createTypedArray(@NonNull Parcelable.Creator<T> c) {
+ assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4170,6 +4379,7 @@ public final class Parcel {
}
public final <T> void readTypedArray(@NonNull T[] val, @NonNull Parcelable.Creator<T> c) {
+ assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -4186,6 +4396,7 @@ public final class Parcel {
*/
@Deprecated
public final <T> T[] readTypedArray(Parcelable.Creator<T> c) {
+ assertNotRecycled();
return createTypedArray(c);
}
@@ -4202,6 +4413,7 @@ public final class Parcel {
*/
@Nullable
public final <T> T readTypedObject(@NonNull Parcelable.Creator<T> c) {
+ assertNotRecycled();
if (readInt() != 0) {
return c.createFromParcel(this);
} else {
@@ -4228,6 +4440,7 @@ public final class Parcel {
* @see #readTypedArray
*/
public <T> void readFixedArray(@NonNull T val) {
+ assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (componentType == boolean.class) {
readBooleanArray((boolean[]) val);
@@ -4268,6 +4481,7 @@ public final class Parcel {
*/
public <T, S extends IInterface> void readFixedArray(@NonNull T val,
@NonNull Function<IBinder, S> asInterface) {
+ assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (IInterface.class.isAssignableFrom(componentType)) {
readInterfaceArray((S[]) val, asInterface);
@@ -4294,6 +4508,7 @@ public final class Parcel {
*/
public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
@NonNull Parcelable.Creator<S> c) {
+ assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (Parcelable.class.isAssignableFrom(componentType)) {
readTypedArray((S[]) val, c);
@@ -4351,6 +4566,7 @@ public final class Parcel {
*/
@Nullable
public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
+ assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4424,6 +4640,7 @@ public final class Parcel {
@Nullable
public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
@NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
+ assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4484,6 +4701,7 @@ public final class Parcel {
@Nullable
public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
@NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
+ assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4547,6 +4765,7 @@ public final class Parcel {
*/
public final <T extends Parcelable> void writeParcelableArray(@Nullable T[] value,
int parcelableFlags) {
+ assertNotRecycled();
if (value != null) {
int N = value.length;
writeInt(N);
@@ -4565,6 +4784,7 @@ public final class Parcel {
*/
@Nullable
public final Object readValue(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readValue(loader, /* clazz */ null);
}
@@ -4620,6 +4840,7 @@ public final class Parcel {
*/
@Nullable
public Object readLazyValue(@Nullable ClassLoader loader) {
+ assertNotRecycled();
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
@@ -5022,6 +5243,7 @@ public final class Parcel {
@Deprecated
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readParcelableInternal(loader, /* clazz */ null);
}
@@ -5041,6 +5263,7 @@ public final class Parcel {
*/
@Nullable
public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
return readParcelableInternal(loader, clazz);
}
@@ -5069,6 +5292,7 @@ public final class Parcel {
@Nullable
public final <T extends Parcelable> T readCreator(@NonNull Parcelable.Creator<?> creator,
@Nullable ClassLoader loader) {
+ assertNotRecycled();
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
@@ -5096,6 +5320,7 @@ public final class Parcel {
@Deprecated
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readParcelableCreatorInternal(loader, /* clazz */ null);
}
@@ -5116,6 +5341,7 @@ public final class Parcel {
@Nullable
public <T> Parcelable.Creator<T> readParcelableCreator(
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
return readParcelableCreatorInternal(loader, clazz);
}
@@ -5238,6 +5464,7 @@ public final class Parcel {
@Deprecated
@Nullable
public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
+ assertNotRecycled();
return readParcelableArrayInternal(loader, /* clazz */ null);
}
@@ -5258,6 +5485,7 @@ public final class Parcel {
@SuppressLint({"ArrayReturn", "NullableCollection"})
@Nullable
public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ assertNotRecycled();
return readParcelableArrayInternal(loader, requireNonNull(clazz));
}
@@ -5291,6 +5519,7 @@ public final class Parcel {
@Deprecated
@Nullable
public Serializable readSerializable() {
+ assertNotRecycled();
return readSerializableInternal(/* loader */ null, /* clazz */ null);
}
@@ -5307,6 +5536,7 @@ public final class Parcel {
*/
@Nullable
public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ assertNotRecycled();
Objects.requireNonNull(clazz);
return readSerializableInternal(
loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -5548,6 +5778,7 @@ public final class Parcel {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
@Nullable ClassLoader loader) {
+ assertNotRecycled();
final int N = readInt();
if (N < 0) {
return;
@@ -5564,6 +5795,7 @@ public final class Parcel {
*/
@UnsupportedAppUsage
public @Nullable ArraySet<? extends Object> readArraySet(@Nullable ClassLoader loader) {
+ assertNotRecycled();
final int size = readInt();
if (size < 0) {
return null;
@@ -5703,6 +5935,7 @@ public final class Parcel {
* @hide For testing
*/
public long getOpenAshmemSize() {
+ assertNotRecycled();
return nativeGetOpenAshmemSize(mNativePtr);
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 1e965c5db7ae..9b8551bf134c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -85,6 +85,51 @@ public class ArrayUtils {
}
/**
+ * This is like <code>new byte[length]</code>, but it allocates the array as non-movable. This
+ * prevents copies of the data from being left on the Java heap as a result of heap compaction.
+ * Use this when the array will contain sensitive data such as a password or cryptographic key
+ * that needs to be wiped from memory when no longer needed. The owner of the array is still
+ * responsible for the zeroization; {@link #zeroize(byte[])} should be used to do so.
+ *
+ * @param length the length of the array to allocate
+ * @return the new array
+ */
+ public static byte[] newNonMovableByteArray(int length) {
+ return (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, length);
+ }
+
+ /**
+ * Like {@link #newNonMovableByteArray(int)}, but allocates a char array.
+ *
+ * @param length the length of the array to allocate
+ * @return the new array
+ */
+ public static char[] newNonMovableCharArray(int length) {
+ return (char[]) VMRuntime.getRuntime().newNonMovableArray(char.class, length);
+ }
+
+ /**
+ * Zeroizes a byte array as securely as possible. Use this when the array contains sensitive
+ * data such as a password or cryptographic key.
+ * <p>
+ * This zeroizes the array in a way that is guaranteed to not be optimized out by the compiler.
+ * If supported by the architecture, it zeroizes the data not just in the L1 data cache but also
+ * in other levels of the memory hierarchy up to and including main memory (but not above that).
+ * <p>
+ * This works on any <code>byte[]</code>, but to ensure that copies of the array aren't left on
+ * the Java heap the array should have been allocated with {@link #newNonMovableByteArray(int)}.
+ * Use on other arrays might also introduce performance anomalies.
+ *
+ * @param array the array to zeroize
+ */
+ public static native void zeroize(byte[] array);
+
+ /**
+ * Like {@link #zeroize(byte[])}, but for char arrays.
+ */
+ public static native void zeroize(char[] array);
+
+ /**
* Checks if the beginnings of two byte arrays are equal.
*
* @param array1 the first byte array
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index e22d9587093b..0ffe2f9e6f2b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -92,6 +92,7 @@ cc_library_shared_for_libandroid_runtime {
"android_view_VelocityTracker.cpp",
"android_view_VerifiedKeyEvent.cpp",
"android_view_VerifiedMotionEvent.cpp",
+ "com_android_internal_util_ArrayUtils.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"core_jni_helpers.cpp",
":deviceproductinfoconstants_aidl",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ac187b08f0f1..78d69f0714e0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -220,6 +220,7 @@ extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
+extern int register_com_android_internal_util_ArrayUtils(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_android_window_WindowInfosListener(JNIEnv* env);
extern int register_android_window_ScreenCapture(JNIEnv* env);
@@ -1621,6 +1622,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_com_android_internal_security_VerityUtils),
+ REG_JNI(register_com_android_internal_util_ArrayUtils),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
diff --git a/core/jni/com_android_internal_util_ArrayUtils.cpp b/core/jni/com_android_internal_util_ArrayUtils.cpp
new file mode 100644
index 000000000000..c69e6c903ee5
--- /dev/null
+++ b/core/jni/com_android_internal_util_ArrayUtils.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#define LOG_TAG "ArrayUtils"
+
+#include <android-base/logging.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <string.h>
+#include <unistd.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static size_t GetCacheLineSize() {
+ long size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+ if (size <= 0) {
+ ALOGE("Unable to determine L1 data cache line size. Assuming 32 bytes");
+ return 32;
+ }
+ return size;
+}
+
+#ifdef __aarch64__
+static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
+ // Execute 'dc cvac' at least once on each cache line in the memory region.
+ //
+ // 'dc cvac' stands for "Data Cache line Clean by Virtual Address to point-of-Coherency".
+ // It writes the cache line back to the "point-of-coherency", i.e. main memory.
+ //
+ // Since the memory region is not guaranteed to be cache-line-aligned, we use an "extra"
+ // instruction after the loop to make sure the last cache line gets covered.
+ for (size_t i = 0; i < size; i += cache_line_size) {
+ asm volatile("dc cvac, %0" ::"r"(p + i));
+ }
+ asm volatile("dc cvac, %0" ::"r"(p + size - 1));
+}
+#elif defined(__i386__) || defined(__x86_64__)
+static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
+ for (size_t i = 0; i < size; i += cache_line_size) {
+ asm volatile("clflush (%0)" ::"r"(p + i));
+ }
+ asm volatile("clflush (%0)" ::"r"(p + size - 1));
+}
+#elif defined(__riscv)
+static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
+ // This should eventually work, but it is not ready to be enabled yet:
+ // 1.) The Android emulator needs to add support for zicbom.
+ // 2.) Kernel needs to enable zicbom in usermode.
+ // 3.) Android clang needs to add zicbom to the target.
+#if 0
+ for (size_t i = 0; i < size; i += cache_line_size) {
+ asm volatile("cbo.clean (%0)" ::"r"(p + i));
+ }
+ asm volatile("cbo.clean (%0)" ::"r"(p + size - 1));
+#endif
+}
+#elif defined(__arm__)
+// arm32 has a cacheflush() syscall, but it is undocumented and only flushes the icache.
+// It is not the same as cacheflush(2) as documented in the Linux man-pages project.
+static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {}
+#else
+#error "Unknown architecture"
+#endif
+
+static void ZeroizePrimitiveArray(JNIEnv* env, jclass clazz, jarray array, size_t component_len) {
+ static const size_t cache_line_size = GetCacheLineSize();
+
+ size_t size = env->GetArrayLength(array) * component_len;
+ if (size == 0) {
+ return;
+ }
+
+ // ART guarantees that GetPrimitiveArrayCritical never copies.
+ jboolean isCopy;
+ void* elems = env->GetPrimitiveArrayCritical(array, &isCopy);
+ CHECK(!isCopy);
+
+#ifdef __BIONIC__
+ memset_explicit(elems, 0, size);
+#else
+ memset(elems, 0, size);
+#endif
+ // Clean the data cache so that the data gets zeroized in main memory right away. Without this,
+ // it might not be written to main memory until the cache line happens to be evicted.
+ CleanDataCache(static_cast<const uint8_t*>(elems), size, cache_line_size);
+
+ env->ReleasePrimitiveArrayCritical(array, elems, /* mode= */ 0);
+}
+
+static void ZeroizeByteArray(JNIEnv* env, jclass clazz, jbyteArray array) {
+ ZeroizePrimitiveArray(env, clazz, array, sizeof(jbyte));
+}
+
+static void ZeroizeCharArray(JNIEnv* env, jclass clazz, jcharArray array) {
+ ZeroizePrimitiveArray(env, clazz, array, sizeof(jchar));
+}
+
+static const JNINativeMethod sMethods[] = {
+ {"zeroize", "([B)V", (void*)ZeroizeByteArray},
+ {"zeroize", "([C)V", (void*)ZeroizeCharArray},
+};
+
+int register_com_android_internal_util_ArrayUtils(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/internal/util/ArrayUtils", sMethods,
+ NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/res/Android.bp b/core/res/Android.bp
index aacd8699c202..903d08b9a2ab 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -162,6 +162,7 @@ android_app {
"android.appwidget.flags-aconfig",
"android.companion.virtualdevice.flags-aconfig",
"android.content.pm.flags-aconfig",
+ "android.location.flags-aconfig",
"android.media.audio-aconfig",
"android.provider.flags-aconfig",
"camera_platform_flags",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7d77ff054fe6..9bb1be85c998 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2145,6 +2145,21 @@
<permission android:name="android.permission.CONTROL_AUTOMOTIVE_GNSS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to bind to a
+ android.service.PopulationDensityProviderService for the purpose of
+ querying population density. This prevents arbitrary clients connecting
+ to the service. The system server checks that the provider's intent
+ service explicitly sets this permission via the android:permission
+ attribute of the service.
+ This is only expected to be possessed by the system server outside of
+ tests.
+ @FlaggedApi(android.location.flags.Flags.FLAG_POPULATION_DENSITY_PROVIDER)
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_POPULATION_DENSITY_PROVIDER_SERVICE"
+ android:featureFlag="android.location.flags.population_density_provider"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
<!-- ======================================= -->
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 3b9f35b1eb68..3c264f15abd3 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -496,4 +496,51 @@ public class ArrayUtilsTest {
// expected
}
}
+
+ // Note: the zeroize() tests only test the behavior that can be tested from a Java test.
+ // They do not verify that no copy of the data is left anywhere.
+
+ @Test
+ @SmallTest
+ public void testZeroizeNonMovableByteArray() {
+ final int length = 10;
+ byte[] array = ArrayUtils.newNonMovableByteArray(length);
+ assertArrayEquals(array, new byte[length]);
+ Arrays.fill(array, (byte) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new byte[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeRegularByteArray() {
+ final int length = 10;
+ byte[] array = new byte[length];
+ assertArrayEquals(array, new byte[length]);
+ Arrays.fill(array, (byte) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new byte[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeNonMovableCharArray() {
+ final int length = 10;
+ char[] array = ArrayUtils.newNonMovableCharArray(length);
+ assertArrayEquals(array, new char[length]);
+ Arrays.fill(array, (char) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new char[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeRegularCharArray() {
+ final int length = 10;
+ char[] array = new char[length];
+ assertArrayEquals(array, new char[length]);
+ Arrays.fill(array, (char) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new char[length]);
+ }
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 897fc543517e..a26f5e383586 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -514,7 +514,6 @@ applications that come with the platform
<permission name="android.permission.RENOUNCE_PERMISSIONS" />
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
- <permission name="android.permission.READ_DROPBOX_DATA" />
<permission name="android.permission.READ_LOGS" />
<permission name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
<permission name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 073307c7a2e8..d010c525e099 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -301,10 +301,7 @@ public class Path {
*
* @param bounds Returns the computed bounds of the path's control points.
* @param exact This parameter is no longer used.
- *
- * @deprecated use computeBounds(RectF) instead
*/
- @Deprecated
@SuppressWarnings({"UnusedDeclaration"})
public void computeBounds(@NonNull RectF bounds, boolean exact) {
computeBounds(bounds);
diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp
index 179a32bd1c03..b03a718d4a65 100644
--- a/native/android/display_luts.cpp
+++ b/native/android/display_luts.cpp
@@ -26,8 +26,9 @@
#define CHECK_NOT_NULL(name) \
LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
-ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension,
- int32_t key) {
+ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length,
+ ADisplayLuts_Dimension dimension,
+ ADisplayLuts_SamplingKey key) {
CHECK_NOT_NULL(buffer);
LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT,
"the lut raw buffer length is too big to handle");
@@ -64,7 +65,7 @@ void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) {
ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) {
CHECK_NOT_NULL(entry);
- return static_cast<ADisplayLuts_Dimension>(entry->properties.dimension);
+ return entry->properties.dimension;
}
int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) {
@@ -74,7 +75,7 @@ int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) {
ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) {
CHECK_NOT_NULL(entry);
- return static_cast<ADisplayLuts_SamplingKey>(entry->properties.samplingKey);
+ return entry->properties.samplingKey;
}
const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 6bca1456db3a..4fe0b80f3951 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -64,6 +64,8 @@ static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_RGB) ==
static_cast<int>(android::gui::LutProperties::SamplingKey::RGB));
static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) ==
static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_CIE_Y) ==
+ static_cast<int>(android::gui::LutProperties::SamplingKey::CIE_Y));
Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
return reinterpret_cast<Transaction*>(aSurfaceTransaction);
diff --git a/nfc/tests/src/android/nfc/NdefRecordTest.java b/nfc/tests/src/android/nfc/NdefRecordTest.java
index 231e939b4fbe..044c67448329 100644
--- a/nfc/tests/src/android/nfc/NdefRecordTest.java
+++ b/nfc/tests/src/android/nfc/NdefRecordTest.java
@@ -24,6 +24,8 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Locale;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NdefRecordTest {
@@ -56,4 +58,20 @@ public class NdefRecordTest {
assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_URI);
}
+ @Test
+ public void testCreateMime() {
+ NdefRecord ndefRecord = NdefRecord.createMime("text/plain", "example".getBytes());
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_MIME_MEDIA);
+ }
+
+ @Test
+ public void testCreateTextRecord() {
+ String languageCode = Locale.getDefault().getLanguage();
+ NdefRecord ndefRecord = NdefRecord.createTextRecord(languageCode, "testdata");
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN);
+ assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_TEXT);
+ }
+
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0ec5571a7b8f..fa6e2dbe02f3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -65,7 +65,6 @@
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
- <uses-permission android:name="android.permission.READ_DROPBOX_DATA" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
<uses-permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 02e7b5f96866..e445884d7051 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -427,6 +427,18 @@ flag {
}
}
+
+flag {
+ name: "status_bar_chips_modernization"
+ namespace: "systemui"
+ description: "Deprecate OngoingCallController and implement OngoingActivityChips"
+ "in compose"
+ bug: "372657935"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
flag {
name: "status_bar_use_repos_for_call_chip"
namespace: "systemui"
@@ -1204,6 +1216,13 @@ flag {
}
flag {
+ name: "communal_responsive_grid"
+ namespace: "systemui"
+ description: "Enables responsive grid on glanceable hub"
+ bug: "378171351"
+}
+
+flag {
name: "communal_standalone_support"
namespace: "systemui"
description: "Support communal features without a dock"
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
new file mode 100644
index 000000000000..2f83d82bbec7
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 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.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.getContainingUFile
+
+class ShadeDisplayAwareDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableUastTypes() = listOf(UClass::class.java)
+
+ override fun createUastHandler(context: JavaContext) =
+ object : UElementHandler() {
+ override fun visitClass(node: UClass) {
+ for (constructor in node.constructors) {
+ // Visit all injected constructors in shade-relevant packages
+ if (!constructor.hasAnnotation(INJECT_ANNOTATION)) continue
+ if (!isInRelevantShadePackage(node)) continue
+ if (IGNORED_PACKAGES.contains(node.qualifiedName)) continue
+
+ // Check the any context-dependent parameter to see if it has @ShadeDisplayAware
+ // annotation
+ for (parameter in constructor.parameterList.parameters) {
+ val shouldReport =
+ CONTEXT_DEPENDENT_SHADE_CLASSES.contains(
+ parameter.type.canonicalText
+ ) && !parameter.hasAnnotation(SHADE_DISPLAY_AWARE_ANNOTATION)
+ if (shouldReport) {
+ context.report(
+ issue = ISSUE,
+ scope = parameter.declarationScope,
+ location = context.getNameLocation(parameter),
+ message = reportMsg(className = parameter.type.presentableText),
+ )
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val INJECT_ANNOTATION = "javax.inject.Inject"
+ private const val SHADE_DISPLAY_AWARE_ANNOTATION =
+ "com.android.systemui.shade.ShadeDisplayAware"
+
+ private val CONTEXT_DEPENDENT_SHADE_CLASSES =
+ setOf(
+ "android.content.Context",
+ "android.view.WindowManager",
+ "android.view.LayoutInflater",
+ "android.content.res.Resources",
+ "com.android.systemui.common.ui.ConfigurationState",
+ "com.android.systemui.statusbar.policy.ConfigurationController",
+ "com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor",
+ )
+
+ private val SHADE_WINDOW_PACKAGES =
+ listOf(
+ "com.android.systemui.biometrics",
+ "com.android.systemui.bouncer",
+ "com.android.systemui.keyboard.docking.ui.viewmodel",
+ "com.android.systemui.qs",
+ "com.android.systemui.shade",
+ "com.android.systemui.statusbar.notification",
+ "com.android.systemui.unfold.domain.interactor",
+ )
+
+ private val IGNORED_PACKAGES =
+ setOf(
+ "com.android.systemui.biometrics.UdfpsController",
+ "com.android.systemui.qs.customize.TileAdapter",
+ )
+
+ private fun isInRelevantShadePackage(node: UClass): Boolean {
+ val packageName = node.getContainingUFile()?.packageName
+ if (packageName.isNullOrBlank()) return false
+ return SHADE_WINDOW_PACKAGES.any { relevantPackage ->
+ packageName.startsWith(relevantPackage)
+ }
+ }
+
+ private fun reportMsg(className: String) =
+ """UI elements of the shade window
+ |should use ShadeDisplayAware-annotated $className, as the shade might move between windows, and only
+ |@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
+ |might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
+ |If the usage of $className is not related to display specific configuration or UI, then there is
+ |technically no need to use the annotation, and you can annotate the class with
+ |@SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")
+ |"""
+ .trimMargin()
+
+ @JvmField
+ val ISSUE: Issue =
+ Issue.create(
+ id = "ShadeDisplayAwareContextChecker",
+ briefDescription = "Using non-ShadeDisplayAware component within shade",
+ explanation =
+ """
+ Any context-dependent components (Resources, LayoutInflater, ConfigurationState,
+ etc.) being injected into Shade-relevant classes must have the @ShadeDisplayAware
+ annotation to ensure they work with when the shade is moved to a different display.
+ When the shade is moved, the configuration might change, and only @ShadeDisplayAware
+ components will update accordingly to reflect the new display.
+ """
+ .trimIndent(),
+ category = Category.CORRECTNESS,
+ priority = 8,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(ShadeDisplayAwareDetector::class.java, Scope.JAVA_FILE_SCOPE),
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index a1f4f5507e5f..6d18f9377806 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -46,8 +46,9 @@ class SystemUIIssueRegistry : IssueRegistry() {
DemotingTestWithoutBugDetector.ISSUE,
TestFunctionNameViolationDetector.ISSUE,
MissingApacheLicenseDetector.ISSUE,
+ ShadeDisplayAwareDetector.ISSUE,
RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING,
- RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR
+ RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR,
)
override val api: Int
@@ -60,6 +61,6 @@ class SystemUIIssueRegistry : IssueRegistry() {
Vendor(
vendorName = "Android",
feedbackUrl = "http://b/issues/new?component=78010",
- contact = "jernej@google.com"
+ contact = "jernej@google.com",
)
}
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
new file mode 100644
index 000000000000..58ad363f5b57
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2024 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestMode
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class ShadeDisplayAwareDetectorTest : SystemUILintDetectorTest() {
+ override fun getDetector(): Detector = ShadeDisplayAwareDetector()
+
+ override fun getIssues(): List<Issue> = listOf(ShadeDisplayAwareDetector.ISSUE)
+
+ private val qsContext: TestFile =
+ java(
+ """
+ package com.android.systemui.qs.dagger;
+
+ import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+ import java.lang.annotation.Retention;
+
+ @Retention(RUNTIME) public @interface QSThemedContext {}
+ """
+ )
+ .indented()
+
+ private val injectStub: TestFile =
+ kotlin(
+ """
+ package javax.inject
+
+ @Retention(AnnotationRetention.RUNTIME) annotation class Inject
+ """
+ )
+ .indented()
+
+ private val shadeDisplayAwareStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.shade
+
+ @Retention(AnnotationRetention.RUNTIME) annotation class ShadeDisplayAware
+ """
+ )
+ .indented()
+
+ private val configStateStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.common.ui
+
+ class ConfigurationState
+ """
+ )
+ .indented()
+
+ private val configControllerStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.statusbar.policy
+
+ class ConfigurationController
+ """
+ )
+ .indented()
+
+ private val configInteractorStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.common.ui.domain.interactor
+
+ class ConfigurationInteractor
+ """
+ )
+ .indented()
+
+ private val otherStubs =
+ arrayOf(
+ injectStub,
+ qsContext,
+ shadeDisplayAwareStub,
+ configStateStub,
+ configControllerStub,
+ configInteractorStub,
+ )
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withRelevantParameter_withoutAnnotation() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.Context
+
+ class ExampleClass
+ @Inject
+ constructor(private val context: Context)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectErrorCount(1)
+ .expectContains(errorMsgString(8, "Context"))
+ .expectContains("[ShadeDisplayAwareContextChecker]")
+ .expectContains(
+ "constructor(private val context: Context)\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains("1 errors, 0 warnings")
+ }
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withMultipleRelevantParameters_withoutAnnotation() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.Context
+ import android.content.res.Resources
+ import android.view.LayoutInflater
+ import android.view.WindowManager
+ import com.android.systemui.common.ui.ConfigurationState
+ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+ import com.android.systemui.statusbar.policy.ConfigurationController
+
+ class ExampleClass
+ @Inject
+ constructor(
+ private val context: Context,
+ private val inflater: LayoutInflater,
+ private val windowManager: WindowManager,
+ private val configState: ConfigurationState,
+ private val configController: ConfigurationController,
+ private val configInteractor: ConfigurationInteractor,
+ )
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectErrorCount(6)
+ .expectContains(errorMsgString(lineNumber = 15, className = "Context"))
+ .expectContains(
+ "private val context: Context,\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains(errorMsgString(lineNumber = 16, className = "LayoutInflater"))
+ .expectContains(
+ "private val inflater: LayoutInflater,\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains(errorMsgString(lineNumber = 17, className = "WindowManager"))
+ .expectContains(
+ "private val windowManager: WindowManager,\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains(errorMsgString(lineNumber = 18, className = "ConfigurationState"))
+ .expectContains(
+ "private val configState: ConfigurationState,\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains(errorMsgString(lineNumber = 19, className = "ConfigurationController"))
+ .expectContains(
+ "private val configController: ConfigurationController,\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains(errorMsgString(lineNumber = 20, className = "ConfigurationInteractor"))
+ .expectContains(
+ "private val configInteractor: ConfigurationInteractor,\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ )
+ .expectContains(" [ShadeDisplayAwareContextChecker]")
+ }
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withRelevantParameter_withAnnotation() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.Context
+ import com.android.systemui.shade.ShadeDisplayAware
+
+ class ExampleClass
+ @Inject
+ constructor(@ShadeDisplayAware private val context: Context)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withoutRelevantParameter_withoutAnnotation() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.ContextWrapper
+
+ class ExampleClass
+ @Inject
+ constructor(private val contextWrapper: ContextWrapper)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun injectedConstructor_notInRelevantPackage_withRelevantParameter_withoutAnnotation() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.keyboard
+
+ import javax.inject.Inject
+ import android.content.Context
+
+ class ExampleClass @Inject constructor(private val context: Context)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun nonInjectedConstructor_inRelevantPackage_withRelevantParameter_withoutAnnotation() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import android.content.Context
+
+ class ExampleClass(private val context: Context)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withRelevantParameter_withoutAnnotation_suppressed() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.Context
+
+ @Suppress("ShadeDisplayAwareContextChecker")
+ class ExampleClass
+ @Inject
+ constructor(
+ private val context: Context
+ )
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun injectedConstructor_inExemptPackage_withRelevantParameter_withoutAnnotation() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package com.android.systemui.qs.customize;
+
+ import javax.inject.Inject;
+ import com.android.systemui.qs.dagger.QSThemedContext;
+ import android.content.Context;
+
+ public class TileAdapter {
+ @Inject
+ public TileAdapter(@QSThemedContext Context context) {}
+ }
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ private fun errorMsgString(lineNumber: Int, className: String) =
+ """
+ src/com/android/systemui/shade/example/ExampleClass.kt:$lineNumber: Error: UI elements of the shade window
+ should use ShadeDisplayAware-annotated $className, as the shade might move between windows, and only
+ @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
+ might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
+ If the usage of $className is not related to display specific configuration or UI, then there is
+ technically no need to use the annotation, and you can annotate the class with
+ @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")
+ """
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index f3cf521f5fbc..573e5ca5e2d5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -66,6 +66,7 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
@@ -169,6 +170,7 @@ import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.internal.R.dimen.system_app_widget_background_radius
import com.android.systemui.Flags
+import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.Flags.communalTimerFlickerFix
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -194,7 +196,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import kotlin.math.max
import kotlin.math.min
-import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -693,7 +694,12 @@ private fun ResizableItemFrameWrapper(
onResize = onResize,
minHeightPx = minHeightPx,
maxHeightPx = maxHeightPx,
- resizeMultiple = CommunalContentSize.HALF.span,
+ resizeMultiple =
+ if (communalResponsiveGrid()) {
+ 1
+ } else {
+ CommunalContentSize.FixedSize.HALF.span
+ },
) {
content(Modifier)
}
@@ -701,14 +707,22 @@ private fun ResizableItemFrameWrapper(
}
@Composable
-fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): WidgetSizeInfo {
+fun calculateWidgetSize(
+ cellHeight: Dp?,
+ availableHeight: Dp?,
+ item: CommunalContentModel,
+ isResizable: Boolean,
+): WidgetSizeInfo {
val density = LocalDensity.current
+ val minHeight = cellHeight ?: CommunalContentSize.FixedSize.HALF.dp()
+ val maxHeight = availableHeight ?: CommunalContentSize.FixedSize.FULL.dp()
+
return if (isResizable && item is CommunalContentModel.WidgetContent.Widget) {
with(density) {
val minHeightPx =
(min(item.providerInfo.minResizeHeight, item.providerInfo.minHeight)
- .coerceAtLeast(CommunalContentSize.HALF.dp().toPx().roundToInt()))
+ .coerceAtLeast(minHeight.roundToPx()))
val maxHeightPx =
(if (item.providerInfo.maxResizeHeight > 0) {
@@ -716,7 +730,7 @@ fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): Widge
} else {
Int.MAX_VALUE
})
- .coerceIn(minHeightPx, CommunalContentSize.FULL.dp().toPx().roundToInt())
+ .coerceIn(minHeightPx, maxHeight.roundToPx())
WidgetSizeInfo(minHeightPx, maxHeightPx)
}
@@ -725,6 +739,37 @@ fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): Widge
}
}
+@Composable
+private fun HorizontalGridWrapper(
+ contentPadding: PaddingValues,
+ gridState: LazyGridState,
+ modifier: Modifier = Modifier,
+ content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit,
+) {
+ if (communalResponsiveGrid()) {
+ ResponsiveLazyHorizontalGrid(
+ cellAspectRatio = 1.5f,
+ modifier = modifier,
+ state = gridState,
+ minContentPadding = contentPadding,
+ minHorizontalArrangement = Dimensions.ItemSpacing,
+ minVerticalArrangement = Dimensions.ItemSpacing,
+ content = content,
+ )
+ } else {
+ LazyHorizontalGrid(
+ modifier = modifier,
+ state = gridState,
+ rows = GridCells.Fixed(CommunalContentSize.FixedSize.FULL.span),
+ contentPadding = contentPadding,
+ horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
+ verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
+ ) {
+ content(null)
+ }
+ }
+}
+
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun BoxScope.CommunalHubLazyGrid(
@@ -778,28 +823,32 @@ private fun BoxScope.CommunalHubLazyGrid(
// Since the grid has its own listener for in-grid drag events, we use a separate element
// for android drag events.
Box(Modifier.fillMaxSize().dragAndDropTarget(dragAndDropTargetState)) {}
+ } else if (communalResponsiveGrid()) {
+ gridModifier = gridModifier.fillMaxSize()
} else {
gridModifier = gridModifier.height(hubDimensions.GridHeight)
}
- val itemArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing)
- LazyHorizontalGrid(
+ HorizontalGridWrapper(
modifier = gridModifier,
- state = gridState,
- rows = GridCells.Fixed(CommunalContentSize.FULL.span),
+ gridState = gridState,
contentPadding = contentPadding,
- horizontalArrangement = itemArrangement,
- verticalArrangement = itemArrangement,
- ) {
+ ) { sizeInfo ->
itemsIndexed(
items = list,
key = { _, item -> item.key },
contentType = { _, item -> item.key },
- span = { _, item -> GridItemSpan(item.size.span) },
+ span = { _, item -> GridItemSpan(item.getSpanOrMax(sizeInfo?.gridSize?.height)) },
) { index, item ->
- val size = SizeF(Dimensions.CardWidth.value, item.size.dp().value)
+ val currentItemSpan = item.getSpanOrMax(sizeInfo?.gridSize?.height)
+ val dpSize =
+ if (sizeInfo != null) {
+ DpSize(sizeInfo.cellSize.width, sizeInfo.calculateHeight(currentItemSpan))
+ } else {
+ DpSize(Dimensions.CardWidth, (item.size as CommunalContentSize.FixedSize).dp())
+ }
+ val size = SizeF(dpSize.width.value, dpSize.height.value)
val selected = item.key == selectedKey.value
- val dpSize = DpSize(size.width.dp, size.height.dp)
val isResizable =
if (item is CommunalContentModel.WidgetContent.Widget) {
item.providerInfo.resizeMode and AppWidgetProviderInfo.RESIZE_VERTICAL != 0
@@ -809,7 +858,7 @@ private fun BoxScope.CommunalHubLazyGrid(
val resizeableItemFrameViewModel =
rememberViewModel(
- key = item.size.span,
+ key = currentItemSpan,
traceName = "ResizeableItemFrame.viewModel.$index",
) {
ResizeableItemFrameViewModel()
@@ -822,13 +871,23 @@ private fun BoxScope.CommunalHubLazyGrid(
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
label = "Widget resizing outline alpha",
)
- val widgetSizeInfo = calculateWidgetSize(item, isResizable)
+
+ val widgetSizeInfo =
+ calculateWidgetSize(
+ cellHeight = sizeInfo?.cellSize?.height,
+ availableHeight = sizeInfo?.availableHeight,
+ item = item,
+ isResizable = isResizable,
+ )
ResizableItemFrameWrapper(
key = item.key,
- currentSpan = GridItemSpan(item.size.span),
+ currentSpan = GridItemSpan(currentItemSpan),
gridState = gridState,
gridContentPadding = contentPadding,
- verticalArrangement = itemArrangement,
+ verticalArrangement =
+ Arrangement.spacedBy(
+ sizeInfo?.verticalArrangement ?: Dimensions.ItemSpacing
+ ),
enabled = selected,
alpha = { outlineAlpha },
modifier =
@@ -1686,11 +1745,11 @@ private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingIn
}
}
-private fun CommunalContentSize.dp(): Dp {
+private fun CommunalContentSize.FixedSize.dp(): Dp {
return when (this) {
- CommunalContentSize.FULL -> Dimensions.CardHeightFull
- CommunalContentSize.HALF -> Dimensions.CardHeightHalf
- CommunalContentSize.THIRD -> Dimensions.CardHeightThird
+ CommunalContentSize.FixedSize.FULL -> Dimensions.CardHeightFull
+ CommunalContentSize.FixedSize.HALF -> Dimensions.CardHeightHalf
+ CommunalContentSize.FixedSize.THIRD -> Dimensions.CardHeightThird
}
}
@@ -1709,7 +1768,10 @@ class Dimensions(val context: Context, val config: Configuration) {
val GridTopSpacing: Dp
get() {
val result =
- if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ if (
+ communalResponsiveGrid() ||
+ config.orientation == Configuration.ORIENTATION_LANDSCAPE
+ ) {
114.dp
} else {
val windowMetrics =
@@ -1729,7 +1791,7 @@ class Dimensions(val context: Context, val config: Configuration) {
get() = 530.adjustedDp
val ItemSpacing
- get() = 50.adjustedDp
+ get() = if (communalResponsiveGrid()) 32.adjustedDp else 50.adjustedDp
val CardHeightHalf
get() = (CardHeightFull - ItemSpacing) / 2
@@ -1771,6 +1833,13 @@ class Dimensions(val context: Context, val config: Configuration) {
data class WidgetSizeInfo(val minHeightPx: Int, val maxHeightPx: Int)
+private fun CommunalContentModel.getSpanOrMax(maxSpan: Int?) =
+ if (maxSpan != null) {
+ size.span.coerceAtMost(maxSpan)
+ } else {
+ size.span
+ }
+
private object Colors {
val DisabledColorFilter by lazy { disabledColorMatrix() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index e3310780afd7..3642127d0823 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -147,9 +147,9 @@ fun ResponsiveLazyHorizontalGrid(
SizeInfo(
cellSize = finalSize,
contentPadding = finalContentPadding,
- horizontalArrangement = minHorizontalArrangement,
verticalArrangement = minVerticalArrangement,
maxHeight = maxHeight,
+ gridSize = gridSize,
)
)
}
@@ -176,16 +176,15 @@ private fun calculateClosestSize(maxWidth: Dp, maxHeight: Dp, aspectRatio: Float
* Provides size info of the responsive grid, since the size is dynamic.
*
* @property cellSize The size of each cell in the grid.
- * @property contentPadding The final content padding of the grid.
- * @property horizontalArrangement The space between columns in the grid.
* @property verticalArrangement The space between rows in the grid.
+ * @property gridSize The size of the grid, in cell units.
* @property availableHeight The maximum height an item in the grid may occupy.
*/
data class SizeInfo(
val cellSize: DpSize,
- val contentPadding: PaddingValues,
- val horizontalArrangement: Dp,
val verticalArrangement: Dp,
+ val gridSize: IntSize,
+ private val contentPadding: PaddingValues,
private val maxHeight: Dp,
) {
val availableHeight: Dp
@@ -193,6 +192,11 @@ data class SizeInfo(
maxHeight -
contentPadding.calculateBottomPadding() -
contentPadding.calculateTopPadding()
+
+ /** Calculates the height in dp of a certain number of rows. */
+ fun calculateHeight(numRows: Int): Dp {
+ return numRows * cellSize.height + (numRows - 1) * verticalArrangement
+ }
}
@Composable
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index 7577147a6f16..42694d5ab119 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -32784,4 +32784,972 @@
column="23"/>
</issue>
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, val theme: Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt"
+ line="39"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @NonNull Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java"
+ line="300"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" Context context, DeviceConfigProxy proxy) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java"
+ line="75"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" public AuthController(Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
+ line="716"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @NonNull WindowManager windowManager,"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
+ line="721"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val sysuiContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
+ line="72"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val configurationController: ConfigurationController,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
+ line="74"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main protected val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java"
+ line="46"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main Resources resources,"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java"
+ line="52"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" public BiometricNotificationService(@NonNull Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java"
+ line="148"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt"
+ line="62"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt"
+ line="37"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" c: Context,"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt"
+ line="61"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt"
+ line="52"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt"
+ line="30"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="class CustomTileStatePersisterImpl @Inject constructor(context: Context) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt"
+ line="74"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt"
+ line="18"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt"
+ line="77"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt"
+ line="68"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt"
+ line="38"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt"
+ line="37"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt"
+ line="42"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt"
+ line="95"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt"
+ line="142"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt"
+ line="44"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @NonNull final Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
+ line="186"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" ConfigurationController configurationController,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
+ line="192"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="class IconBuilder @Inject constructor(private val context: Context) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt"
+ line="27"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt"
+ line="31"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val windowManager: WindowManager,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
+ line="39"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
+ line="40"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt"
+ line="41"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated LayoutInflater, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of LayoutInflater is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val layoutInflater: LayoutInflater"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt"
+ line="29"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="class MinimumTilesResourceRepository @Inject constructor(@Main resources: Resources) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt"
+ line="38"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt"
+ line="38"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt"
+ line="42"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" public NotificationGutsManager(Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java"
+ line="137"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt"
+ line="47"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt"
+ line="53"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(val context: Context) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt"
+ line="27"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" ConfigurationController configurationController,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java"
+ line="737"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt"
+ line="63"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" Builder(@Main Resources resources, ViewConfiguration viewConfiguration,"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java"
+ line="563"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" public PackageManagerAdapter(Context context) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java"
+ line="45"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt"
+ line="36"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt"
+ line="74"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt"
+ line="41"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt"
+ line="82"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt"
+ line="32"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt"
+ line="36"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" Factory(Context context, QSCustomizerController qsCustomizerController) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java"
+ line="99"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt"
+ line="32"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt"
+ line="36"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt"
+ line="47"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt"
+ line="36"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt"
+ line="51"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt"
+ line="33"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated LayoutInflater, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of LayoutInflater is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val layoutInflater: LayoutInflater,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt"
+ line="43"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" context: Context"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt"
+ line="35"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt"
+ line="63"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt"
+ line="53"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
+ line="51"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of WindowManager is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" windowManager: WindowManager,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
+ line="53"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val applicationContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt"
+ line="60"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt"
+ line="65"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt"
+ line="91"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(context: Context, val shadeViewController: ShadeViewController) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt"
+ line="30"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/StockTilesRepository.kt"
+ line="31"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val context: Context"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt"
+ line="33"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt"
+ line="95"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt"
+ line="33"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Context is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Application private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt"
+ line="49"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of ConfigurationController is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" private val configurationController: ConfigurationController,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt"
+ line="43"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1="constructor(@Main private val resources: Resources, private val theme: Theme) :"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt"
+ line="37"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window&#xA;should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only&#xA;@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so&#xA;might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).&#xA;If the usage of Resources is not related to display specific configuration or UI, then there is&#xA;technically no need to use the annotation, and you can annotate the class with&#xA;@SuppressLint(&quot;ShadeDisplayAwareContextChecker&quot;)/@Suppress(&quot;ShadeDisplayAwareContextChecker&quot;)"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt"
+ line="36"
+ column="5"/>
+ </issue>
+
</issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
index 596db0767867..f1c58a2aeac9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
@@ -24,6 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.db.DefaultWidgetPopulation.SkipReason.RESTORED_FROM_BACKUP
+import com.android.systemui.communal.shared.model.SpanValue
import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
@@ -117,7 +118,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() {
componentName = defaultWidgets[0],
rank = 0,
userSerialNumber = 0,
- spanY = 3,
+ spanY = SpanValue.Fixed(3),
)
verify(communalWidgetDao)
.addWidget(
@@ -125,7 +126,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() {
componentName = defaultWidgets[1],
rank = 1,
userSerialNumber = 0,
- spanY = 3,
+ spanY = SpanValue.Fixed(3),
)
verify(communalWidgetDao)
.addWidget(
@@ -133,7 +134,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() {
componentName = defaultWidgets[2],
rank = 2,
userSerialNumber = 0,
- spanY = 3,
+ spanY = SpanValue.Fixed(3),
)
}
@@ -155,7 +156,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() {
componentName = any(),
rank = anyInt(),
userSerialNumber = anyInt(),
- spanY = anyInt(),
+ spanY = any(),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
index 55d7d08e8519..335e39902983 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
@@ -24,11 +24,15 @@ import android.content.ComponentName
import android.content.applicationContext
import android.graphics.Bitmap
import android.os.UserHandle
+import android.os.UserManager
import android.os.userManager
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
+import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.data.repository.fakePackageChangeRepository
import com.android.systemui.common.shared.model.PackageInstallSession
@@ -40,11 +44,15 @@ import com.android.systemui.communal.data.db.defaultWidgetPopulation
import com.android.systemui.communal.nano.CommunalHubState
import com.android.systemui.communal.proto.toByteArray
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.SpanValue
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.communal.widgets.widgetConfiguratorFail
import com.android.systemui.communal.widgets.widgetConfiguratorSuccess
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.LogBuffer
@@ -52,48 +60,55 @@ import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
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.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
- @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
- @Mock private lateinit var providerInfoA: AppWidgetProviderInfo
- @Mock private lateinit var providerInfoB: AppWidgetProviderInfo
- @Mock private lateinit var providerInfoC: AppWidgetProviderInfo
- @Mock private lateinit var communalWidgetHost: CommunalWidgetHost
- @Mock private lateinit var communalWidgetDao: CommunalWidgetDao
- @Mock private lateinit var backupManager: BackupManager
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalWidgetRepositoryLocalImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val appWidgetHost = mock<CommunalAppWidgetHost>()
+ private val providerInfoA = mock<AppWidgetProviderInfo>()
+ private val providerInfoB = mock<AppWidgetProviderInfo>()
+ private val providerInfoC = mock<AppWidgetProviderInfo>()
private val communalHubStateCaptor = argumentCaptor<CommunalHubState>()
private val componentNameCaptor = argumentCaptor<ComponentName>()
- private lateinit var backupUtils: CommunalBackupUtils
- private lateinit var logBuffer: LogBuffer
- private lateinit var fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>>
- private lateinit var fakeProviders: MutableStateFlow<Map<Int, AppWidgetProviderInfo?>>
+ private val Kosmos.communalWidgetHost by
+ Kosmos.Fixture {
+ mock<CommunalWidgetHost> { on { appWidgetProviders } doReturn fakeProviders }
+ }
+ private val Kosmos.communalWidgetDao by
+ Kosmos.Fixture { mock<CommunalWidgetDao> { on { getWidgets() } doReturn fakeWidgets } }
+
+ private val Kosmos.backupManager by Kosmos.Fixture { mock<BackupManager>() }
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val packageChangeRepository = kosmos.fakePackageChangeRepository
- private val userManager = kosmos.userManager
+ private val Kosmos.backupUtils: CommunalBackupUtils by
+ Kosmos.Fixture { CommunalBackupUtils(applicationContext) }
+
+ private val Kosmos.logBuffer: LogBuffer by
+ Kosmos.Fixture { logcatLogBuffer(name = "CommunalWidgetRepoLocalImplTest") }
+
+ private val Kosmos.fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>> by
+ Kosmos.Fixture { MutableStateFlow(emptyMap()) }
+
+ private val Kosmos.fakeProviders: MutableStateFlow<Map<Int, AppWidgetProviderInfo?>> by
+ Kosmos.Fixture { MutableStateFlow(emptyMap()) }
private val mainUser = UserHandle(0)
private val workProfile = UserHandle(10)
@@ -105,48 +120,49 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
"com.android.fake/WidgetProviderC",
)
- private lateinit var underTest: CommunalWidgetRepositoryLocalImpl
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- fakeWidgets = MutableStateFlow(emptyMap())
- fakeProviders = MutableStateFlow(emptyMap())
- logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoLocalImplTest")
- backupUtils = CommunalBackupUtils(kosmos.applicationContext)
-
- setAppWidgetIds(emptyList())
-
- overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
-
- whenever(communalWidgetDao.getWidgets()).thenReturn(fakeWidgets)
- whenever(communalWidgetHost.appWidgetProviders).thenReturn(fakeProviders)
- whenever(userManager.mainUser).thenReturn(mainUser)
-
- restoreUser(mainUser)
-
- underTest =
+ private val Kosmos.underTest by
+ Kosmos.Fixture {
CommunalWidgetRepositoryLocalImpl(
appWidgetHost,
testScope.backgroundScope,
- kosmos.testDispatcher,
+ testDispatcher,
communalWidgetHost,
communalWidgetDao,
logBuffer,
backupManager,
backupUtils,
- packageChangeRepository,
+ fakePackageChangeRepository,
userManager,
- kosmos.defaultWidgetPopulation,
+ defaultWidgetPopulation,
)
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setUp() {
+ kosmos.userManager = mock<UserManager> { on { mainUser } doReturn mainUser }
+ setAppWidgetIds(emptyList())
+ overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
+ restoreUser(mainUser)
}
@Test
fun communalWidgets_queryWidgetsFromDb() =
- testScope.runTest {
+ kosmos.runTest {
val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1)
val communalWidgetItemEntry =
- CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0, 3)
+ CommunalWidgetItem(
+ uid = 1L,
+ widgetId = 1,
+ componentName = "pk_name/cls_name",
+ itemId = 1L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ )
fakeWidgets.value = mapOf(communalItemRankEntry to communalWidgetItemEntry)
fakeProviders.value = mapOf(1 to providerInfoA)
@@ -158,7 +174,12 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
appWidgetId = communalWidgetItemEntry.widgetId,
providerInfo = providerInfoA,
rank = communalItemRankEntry.rank,
- spanY = communalWidgetItemEntry.spanY,
+ spanY =
+ if (communalResponsiveGrid()) {
+ communalWidgetItemEntry.spanYNew
+ } else {
+ communalWidgetItemEntry.spanY
+ },
)
)
@@ -168,18 +189,50 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun communalWidgets_widgetsWithoutMatchingProvidersAreSkipped() =
- testScope.runTest {
+ kosmos.runTest {
// Set up 4 widgets, but widget 3 and 4 don't have matching providers
fakeWidgets.value =
mapOf(
CommunalItemRank(uid = 1L, rank = 1) to
- CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3),
+ CommunalWidgetItem(
+ uid = 1L,
+ widgetId = 1,
+ componentName = "pk_1/cls_1",
+ itemId = 1L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
CommunalItemRank(uid = 2L, rank = 2) to
- CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3),
+ CommunalWidgetItem(
+ uid = 2L,
+ widgetId = 2,
+ componentName = "pk_2/cls_2",
+ itemId = 2L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
CommunalItemRank(uid = 3L, rank = 3) to
- CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0, 3),
+ CommunalWidgetItem(
+ uid = 3L,
+ widgetId = 3,
+ componentName = "pk_3/cls_3",
+ itemId = 3L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
CommunalItemRank(uid = 4L, rank = 4) to
- CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0, 3),
+ CommunalWidgetItem(
+ uid = 4L,
+ widgetId = 4,
+ componentName = "pk_4/cls_4",
+ itemId = 4L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
)
fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB)
@@ -191,27 +244,43 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
appWidgetId = 1,
providerInfo = providerInfoA,
rank = 1,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
),
CommunalWidgetContentModel.Available(
appWidgetId = 2,
providerInfo = providerInfoB,
rank = 2,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
),
)
}
@Test
fun communalWidgets_updatedWhenProvidersUpdate() =
- testScope.runTest {
+ kosmos.runTest {
// Set up widgets and providers
fakeWidgets.value =
mapOf(
CommunalItemRank(uid = 1L, rank = 1) to
- CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3),
+ CommunalWidgetItem(
+ uid = 1L,
+ widgetId = 1,
+ componentName = "pk_1/cls_1",
+ itemId = 1L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
CommunalItemRank(uid = 2L, rank = 2) to
- CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3),
+ CommunalWidgetItem(
+ uid = 2L,
+ widgetId = 2,
+ componentName = "pk_2/cls_2",
+ itemId = 2L,
+ userSerialNumber = 0,
+ spanY = 6,
+ spanYNew = 2,
+ ),
)
fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB)
@@ -224,13 +293,13 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
appWidgetId = 1,
providerInfo = providerInfoA,
rank = 1,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
),
CommunalWidgetContentModel.Available(
appWidgetId = 2,
providerInfo = providerInfoB,
rank = 2,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 2 else 6,
),
)
@@ -245,20 +314,20 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
// Verify that provider info updated
providerInfo = providerInfoC,
rank = 1,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
),
CommunalWidgetContentModel.Available(
appWidgetId = 2,
providerInfo = providerInfoB,
rank = 2,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 2 else 6,
),
)
}
@Test
fun addWidget_allocateId_bindWidget_andAddToDb() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val rank = 1
@@ -275,7 +344,8 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
runCurrent()
verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
- verify(communalWidgetDao).addWidget(id, provider, rank, testUserSerialNumber(mainUser))
+ verify(communalWidgetDao)
+ .addWidget(id, provider, rank, testUserSerialNumber(mainUser), SpanValue.Fixed(3))
// Verify backup requested
verify(backupManager).dataChanged()
@@ -283,7 +353,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun addWidget_configurationFails_doNotAddWidgetToDb() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val rank = 1
@@ -301,7 +371,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
verify(communalWidgetDao, never())
- .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt())
+ .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), any())
verify(appWidgetHost).deleteAppWidgetId(id)
// Verify backup not requested
@@ -310,7 +380,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun addWidget_configurationThrowsError_doNotAddWidgetToDb() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val rank = 1
@@ -330,7 +400,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
verify(communalWidgetDao, never())
- .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt())
+ .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), any())
verify(appWidgetHost).deleteAppWidgetId(id)
// Verify backup not requested
@@ -339,7 +409,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun addWidget_configurationNotRequired_doesNotConfigure_addWidgetToDb() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val rank = 1
@@ -356,7 +426,8 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
runCurrent()
verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
- verify(communalWidgetDao).addWidget(id, provider, rank, testUserSerialNumber(mainUser))
+ verify(communalWidgetDao)
+ .addWidget(id, provider, rank, testUserSerialNumber(mainUser), SpanValue.Fixed(3))
// Verify backup requested
verify(backupManager).dataChanged()
@@ -364,7 +435,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun deleteWidget_deleteFromDbTrue_alsoDeleteFromHost() =
- testScope.runTest {
+ kosmos.runTest {
val id = 1
whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(true)
underTest.deleteWidget(id)
@@ -379,7 +450,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun deleteWidget_deleteFromDbFalse_doesNotDeleteFromHost() =
- testScope.runTest {
+ kosmos.runTest {
val id = 1
whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(false)
underTest.deleteWidget(id)
@@ -394,7 +465,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun reorderWidgets_queryDb() =
- testScope.runTest {
+ kosmos.runTest {
val widgetIdToRankMap = mapOf(104 to 1, 103 to 2, 101 to 3)
underTest.updateWidgetOrder(widgetIdToRankMap)
runCurrent()
@@ -407,7 +478,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_deleteStateFileIfRestoreFails() =
- testScope.runTest {
+ kosmos.runTest {
// Write a state file that is invalid, and verify it is written
backupUtils.writeBytesToDisk(byteArrayOf(1, 2, 3, 4, 5, 6))
assertThat(backupUtils.fileExists()).isTrue()
@@ -422,7 +493,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_deleteStateFileAfterWidgetsRestored() =
- testScope.runTest {
+ kosmos.runTest {
// Write a state file, and verify it is written
backupUtils.writeBytesToDisk(fakeState.toByteArray())
assertThat(backupUtils.fileExists()).isTrue()
@@ -443,7 +514,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_restoredWidgetsNotRegisteredWithHostAreSkipped() =
- testScope.runTest {
+ kosmos.runTest {
// Write fake state to file
backupUtils.writeBytesToDisk(fakeState.toByteArray())
@@ -470,7 +541,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_registeredWidgetsNotRestoredAreRemoved() =
- testScope.runTest {
+ kosmos.runTest {
// Write fake state to file
backupUtils.writeBytesToDisk(fakeState.toByteArray())
@@ -504,7 +575,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_onlySomeWidgetsGotNewIds() =
- testScope.runTest {
+ kosmos.runTest {
// Write fake state to file
backupUtils.writeBytesToDisk(fakeState.toByteArray())
@@ -536,7 +607,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_undefinedUser_restoredAsMain() =
- testScope.runTest {
+ kosmos.runTest {
// Write two widgets to file, both of which have user serial number undefined.
val fakeState =
CommunalHubState().apply {
@@ -584,7 +655,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_workProfileNotRestored_widgetSkipped() =
- testScope.runTest {
+ kosmos.runTest {
// Write fake state to file
backupUtils.writeBytesToDisk(fakeStateWithWorkProfile.toByteArray())
@@ -610,7 +681,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun restoreWidgets_workProfileRestored_manuallyBindWidget() =
- testScope.runTest {
+ kosmos.runTest {
// Write fake state to file
backupUtils.writeBytesToDisk(fakeStateWithWorkProfile.toByteArray())
@@ -649,7 +720,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
componentNameCaptor.capture(),
eq(2),
eq(testUserSerialNumber(workProfile)),
- anyInt(),
+ any(),
)
assertThat(componentNameCaptor.firstValue)
@@ -658,13 +729,29 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Test
fun pendingWidgets() =
- testScope.runTest {
+ kosmos.runTest {
fakeWidgets.value =
mapOf(
CommunalItemRank(uid = 1L, rank = 1) to
- CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3),
+ CommunalWidgetItem(
+ uid = 1L,
+ widgetId = 1,
+ componentName = "pk_1/cls_1",
+ itemId = 1L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
CommunalItemRank(uid = 2L, rank = 2) to
- CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3),
+ CommunalWidgetItem(
+ uid = 2L,
+ widgetId = 2,
+ componentName = "pk_2/cls_2",
+ itemId = 2L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ ),
)
// Widget 1 is installed
@@ -672,7 +759,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
// Widget 2 is pending install
val fakeIcon = mock<Bitmap>()
- packageChangeRepository.setInstallSessions(
+ fakePackageChangeRepository.setInstallSessions(
listOf(
PackageInstallSession(
sessionId = 1,
@@ -690,7 +777,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
appWidgetId = 1,
providerInfo = providerInfoA,
rank = 1,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
),
CommunalWidgetContentModel.Pending(
appWidgetId = 2,
@@ -698,23 +785,31 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
componentName = ComponentName("pk_2", "cls_2"),
icon = fakeIcon,
user = mainUser,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
),
)
}
@Test
fun pendingWidgets_pendingWidgetBecomesAvailableAfterInstall() =
- testScope.runTest {
+ kosmos.runTest {
fakeWidgets.value =
mapOf(
CommunalItemRank(uid = 1L, rank = 1) to
- CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3)
+ CommunalWidgetItem(
+ uid = 1L,
+ widgetId = 1,
+ componentName = "pk_1/cls_1",
+ itemId = 1L,
+ userSerialNumber = 0,
+ spanY = 3,
+ spanYNew = 1,
+ )
)
// Widget 1 is pending install
val fakeIcon = mock<Bitmap>()
- packageChangeRepository.setInstallSessions(
+ fakePackageChangeRepository.setInstallSessions(
listOf(
PackageInstallSession(
sessionId = 1,
@@ -734,12 +829,12 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
componentName = ComponentName("pk_1", "cls_1"),
icon = fakeIcon,
user = mainUser,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
)
)
// Package for widget 1 finished installing
- packageChangeRepository.setInstallSessions(emptyList())
+ fakePackageChangeRepository.setInstallSessions(emptyList())
// Provider info for widget 1 becomes available
fakeProviders.value = mapOf(1 to providerInfoA)
@@ -752,15 +847,32 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
appWidgetId = 1,
providerInfo = providerInfoA,
rank = 1,
- spanY = 3,
+ spanY = if (communalResponsiveGrid()) 1 else 3,
)
)
}
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
- fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup() =
- testScope.runTest {
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
+ fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup_fixed() =
+ kosmos.runTest {
+ val widgetId = 1
+ val newSpanY = 6
+ val widgetIdToRankMap = emptyMap<Int, Int>()
+
+ underTest.resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
+ runCurrent()
+
+ verify(communalWidgetDao)
+ .resizeWidget(widgetId, SpanValue.Fixed(newSpanY), widgetIdToRankMap)
+ verify(backupManager).dataChanged()
+ }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
+ fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup_responsive() =
+ kosmos.runTest {
val widgetId = 1
val newSpanY = 6
val widgetIdToRankMap = emptyMap<Int, Int>()
@@ -768,7 +880,8 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
underTest.resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
runCurrent()
- verify(communalWidgetDao).resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
+ verify(communalWidgetDao)
+ .resizeWidget(widgetId, SpanValue.Responsive(newSpanY), widgetIdToRankMap)
verify(backupManager).dataChanged()
}
@@ -784,13 +897,19 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
}
private fun restoreUser(user: UserHandle) {
- whenever(backupManager.getUserForAncestralSerialNumber(user.identifier.toLong()))
+ whenever(kosmos.backupManager.getUserForAncestralSerialNumber(user.identifier.toLong()))
.thenReturn(user)
- whenever(userManager.getUserSerialNumber(user.identifier))
+ whenever(kosmos.userManager.getUserSerialNumber(user.identifier))
.thenReturn(testUserSerialNumber(user))
}
- private companion object {
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_RESPONSIVE_GRID)
+ }
+
val PROVIDER_INFO_REQUIRES_CONFIGURATION =
AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") }
val PROVIDER_INFO_CONFIGURATION_OPTIONAL =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 611a61a6282c..b9e646fee98f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -24,14 +24,16 @@ import android.content.pm.UserInfo
import android.os.UserHandle
import android.os.UserManager
import android.os.userManager
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
import android.widget.RemoteViews
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
@@ -96,6 +98,8 @@ import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
/**
* This class of test cases assume that communal is enabled. For disabled cases, see
@@ -103,8 +107,8 @@ import org.mockito.MockitoAnnotations
*/
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidJUnit4::class)
-class CommunalInteractorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var mainUser: UserInfo
@Mock private lateinit var secondaryUser: UserInfo
@@ -129,6 +133,10 @@ class CommunalInteractorTest : SysuiTestCase() {
private lateinit var underTest: CommunalInteractor
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -262,71 +270,84 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(widgetContent!![2].appWidgetId).isEqualTo(3)
}
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun smartspaceDynamicSizing_oneCard_fullSize() =
testSmartspaceDynamicSizing(
totalTargets = 1,
- expectedSizes = listOf(CommunalContentSize.FULL),
+ expectedSizes = listOf(CommunalContentSize.FixedSize.FULL),
)
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun smartspace_dynamicSizing_twoCards_halfSize() =
testSmartspaceDynamicSizing(
totalTargets = 2,
- expectedSizes = listOf(CommunalContentSize.HALF, CommunalContentSize.HALF),
+ expectedSizes =
+ listOf(CommunalContentSize.FixedSize.HALF, CommunalContentSize.FixedSize.HALF),
)
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun smartspace_dynamicSizing_threeCards_thirdSize() =
testSmartspaceDynamicSizing(
totalTargets = 3,
expectedSizes =
listOf(
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
),
)
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun smartspace_dynamicSizing_fourCards_threeThirdSizeAndOneFullSize() =
testSmartspaceDynamicSizing(
totalTargets = 4,
expectedSizes =
listOf(
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.FULL,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.FULL,
),
)
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun smartspace_dynamicSizing_fiveCards_threeThirdAndTwoHalfSize() =
testSmartspaceDynamicSizing(
totalTargets = 5,
expectedSizes =
listOf(
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.HALF,
- CommunalContentSize.HALF,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.HALF,
+ CommunalContentSize.FixedSize.HALF,
),
)
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun smartspace_dynamicSizing_sixCards_allThirdSize() =
testSmartspaceDynamicSizing(
totalTargets = 6,
expectedSizes =
listOf(
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
+ CommunalContentSize.FixedSize.THIRD,
),
)
@@ -383,7 +404,9 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(umoContent?.size).isEqualTo(0)
}
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun ongoing_shouldOrderAndSizeByTimestamp() =
testScope.runTest {
// Keyguard showing, and tutorial completed.
@@ -410,15 +433,15 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(ongoingContent?.size).isEqualTo(4)
assertThat(ongoingContent?.get(0)?.key)
.isEqualTo(CommunalContentModel.KEY.smartspace("timer3"))
- assertThat(ongoingContent?.get(0)?.size).isEqualTo(CommunalContentSize.HALF)
+ assertThat(ongoingContent?.get(0)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
assertThat(ongoingContent?.get(1)?.key)
.isEqualTo(CommunalContentModel.KEY.smartspace("timer2"))
- assertThat(ongoingContent?.get(1)?.size).isEqualTo(CommunalContentSize.HALF)
+ assertThat(ongoingContent?.get(1)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
assertThat(ongoingContent?.get(2)?.key).isEqualTo(CommunalContentModel.KEY.umo())
- assertThat(ongoingContent?.get(2)?.size).isEqualTo(CommunalContentSize.HALF)
+ assertThat(ongoingContent?.get(2)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
assertThat(ongoingContent?.get(3)?.key)
.isEqualTo(CommunalContentModel.KEY.smartspace("timer1"))
- assertThat(ongoingContent?.get(3)?.size).isEqualTo(CommunalContentSize.HALF)
+ assertThat(ongoingContent?.get(3)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
}
@Test
@@ -1082,6 +1105,7 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_withoutUpdatingOrder() =
testScope.runTest {
val userInfos = listOf(MAIN_USER_INFO)
@@ -1094,45 +1118,97 @@ class CommunalInteractorTest : SysuiTestCase() {
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
- spanY = CommunalContentSize.HALF.span,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
)
widgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
- spanY = CommunalContentSize.HALF.span,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
)
widgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
- spanY = CommunalContentSize.HALF.span,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
)
val widgetContent by collectLastValue(underTest.widgetContent)
assertThat(widgetContent?.map { it.appWidgetId to it.size })
.containsExactly(
- 1 to CommunalContentSize.HALF,
- 2 to CommunalContentSize.HALF,
- 3 to CommunalContentSize.HALF,
+ 1 to CommunalContentSize.FixedSize.HALF,
+ 2 to CommunalContentSize.FixedSize.HALF,
+ 3 to CommunalContentSize.FixedSize.HALF,
)
.inOrder()
- underTest.resizeWidget(2, CommunalContentSize.FULL.span, emptyMap())
+ underTest.resizeWidget(2, CommunalContentSize.FixedSize.FULL.span, emptyMap())
// Widget 2 should have been resized to FULL
assertThat(widgetContent?.map { it.appWidgetId to it.size })
.containsExactly(
- 1 to CommunalContentSize.HALF,
- 2 to CommunalContentSize.FULL,
- 3 to CommunalContentSize.HALF,
+ 1 to CommunalContentSize.FixedSize.HALF,
+ 2 to CommunalContentSize.FixedSize.FULL,
+ 3 to CommunalContentSize.FixedSize.HALF,
+ )
+ .inOrder()
+ }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
+ fun resizeWidget_withoutUpdatingOrder_responsive() =
+ testScope.runTest {
+ val userInfos = listOf(MAIN_USER_INFO)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ runCurrent()
+
+ // Widgets available.
+ widgetRepository.addWidget(
+ appWidgetId = 1,
+ userId = MAIN_USER_INFO.id,
+ rank = 0,
+ spanY = 1,
+ )
+ widgetRepository.addWidget(
+ appWidgetId = 2,
+ userId = MAIN_USER_INFO.id,
+ rank = 1,
+ spanY = 1,
+ )
+ widgetRepository.addWidget(
+ appWidgetId = 3,
+ userId = MAIN_USER_INFO.id,
+ rank = 2,
+ spanY = 1,
+ )
+
+ val widgetContent by collectLastValue(underTest.widgetContent)
+
+ assertThat(widgetContent?.map { it.appWidgetId to it.size })
+ .containsExactly(
+ 1 to CommunalContentSize.Responsive(1),
+ 2 to CommunalContentSize.Responsive(1),
+ 3 to CommunalContentSize.Responsive(1),
+ )
+ .inOrder()
+
+ underTest.resizeWidget(appWidgetId = 2, spanY = 5, widgetIdToRankMap = emptyMap())
+
+ // Widget 2 should have been resized to FULL
+ assertThat(widgetContent?.map { it.appWidgetId to it.size })
+ .containsExactly(
+ 1 to CommunalContentSize.Responsive(1),
+ 2 to CommunalContentSize.Responsive(5),
+ 3 to CommunalContentSize.Responsive(1),
)
.inOrder()
}
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_andUpdateOrder() =
testScope.runTest {
val userInfos = listOf(MAIN_USER_INFO)
@@ -1145,39 +1221,98 @@ class CommunalInteractorTest : SysuiTestCase() {
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
- spanY = CommunalContentSize.HALF.span,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
+ )
+ widgetRepository.addWidget(
+ appWidgetId = 2,
+ userId = MAIN_USER_INFO.id,
+ rank = 1,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
+ )
+ widgetRepository.addWidget(
+ appWidgetId = 3,
+ userId = MAIN_USER_INFO.id,
+ rank = 2,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
+ )
+
+ val widgetContent by collectLastValue(underTest.widgetContent)
+
+ assertThat(widgetContent?.map { it.appWidgetId to it.size })
+ .containsExactly(
+ 1 to CommunalContentSize.FixedSize.HALF,
+ 2 to CommunalContentSize.FixedSize.HALF,
+ 3 to CommunalContentSize.FixedSize.HALF,
+ )
+ .inOrder()
+
+ underTest.resizeWidget(
+ 2,
+ CommunalContentSize.FixedSize.FULL.span,
+ mapOf(2 to 0, 1 to 1),
+ )
+
+ // Widget 2 should have been resized to FULL and moved to the front of the list
+ assertThat(widgetContent?.map { it.appWidgetId to it.size })
+ .containsExactly(
+ 2 to CommunalContentSize.FixedSize.FULL,
+ 1 to CommunalContentSize.FixedSize.HALF,
+ 3 to CommunalContentSize.FixedSize.HALF,
+ )
+ .inOrder()
+ }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
+ fun resizeWidget_andUpdateOrder_responsive() =
+ testScope.runTest {
+ val userInfos = listOf(MAIN_USER_INFO)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ runCurrent()
+
+ // Widgets available.
+ widgetRepository.addWidget(
+ appWidgetId = 1,
+ userId = MAIN_USER_INFO.id,
+ rank = 0,
+ spanY = 1,
)
widgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
- spanY = CommunalContentSize.HALF.span,
+ spanY = 1,
)
widgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
- spanY = CommunalContentSize.HALF.span,
+ spanY = 1,
)
val widgetContent by collectLastValue(underTest.widgetContent)
assertThat(widgetContent?.map { it.appWidgetId to it.size })
.containsExactly(
- 1 to CommunalContentSize.HALF,
- 2 to CommunalContentSize.HALF,
- 3 to CommunalContentSize.HALF,
+ 1 to CommunalContentSize.Responsive(1),
+ 2 to CommunalContentSize.Responsive(1),
+ 3 to CommunalContentSize.Responsive(1),
)
.inOrder()
- underTest.resizeWidget(2, CommunalContentSize.FULL.span, mapOf(2 to 0, 1 to 1))
+ underTest.resizeWidget(
+ appWidgetId = 2,
+ spanY = 5,
+ widgetIdToRankMap = mapOf(2 to 0, 1 to 1),
+ )
// Widget 2 should have been resized to FULL and moved to the front of the list
assertThat(widgetContent?.map { it.appWidgetId to it.size })
.containsExactly(
- 2 to CommunalContentSize.FULL,
- 1 to CommunalContentSize.HALF,
- 3 to CommunalContentSize.HALF,
+ 2 to CommunalContentSize.Responsive(5),
+ 1 to CommunalContentSize.Responsive(1),
+ 3 to CommunalContentSize.Responsive(1),
)
.inOrder()
}
@@ -1191,9 +1326,15 @@ class CommunalInteractorTest : SysuiTestCase() {
)
}
- private companion object {
- val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
- val USER_INFO_WORK =
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_RESPONSIVE_GRID)
+ }
+
+ private val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ private val USER_INFO_WORK =
UserInfo(
10,
"work",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 3eba8ff4b198..763ea392deb9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -18,12 +18,14 @@ package com.android.systemui.communal.view.viewmodel
import android.content.ComponentName
import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.widget.RemoteViews
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
@@ -248,7 +250,9 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
.isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
}
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun ongoingContent_umoAndOneTimer_sizedAppropriately() =
testScope.runTest {
// Widgets available.
@@ -280,11 +284,13 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(timer).isInstanceOf(CommunalContentModel.Smartspace::class.java)
assertThat(umo).isInstanceOf(CommunalContentModel.Umo::class.java)
- assertThat(timer?.size).isEqualTo(CommunalContentSize.HALF)
- assertThat(umo?.size).isEqualTo(CommunalContentSize.HALF)
+ assertThat(timer?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
+ assertThat(umo?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
}
+ /** TODO(b/378171351): Handle ongoing content in responsive grid. */
@Test
+ @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun ongoingContent_umoAndTwoTimers_sizedAppropriately() =
testScope.runTest {
// Widgets available.
@@ -324,9 +330,9 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(umo).isInstanceOf(CommunalContentModel.Umo::class.java)
// One full-sized timer and a half-sized timer and half-sized UMO.
- assertThat(timer1?.size).isEqualTo(CommunalContentSize.HALF)
- assertThat(timer2?.size).isEqualTo(CommunalContentSize.HALF)
- assertThat(umo?.size).isEqualTo(CommunalContentSize.FULL)
+ assertThat(timer1?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
+ assertThat(timer2?.size).isEqualTo(CommunalContentSize.FixedSize.HALF)
+ assertThat(umo?.size).isEqualTo(CommunalContentSize.FixedSize.FULL)
}
@Test
@@ -891,7 +897,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_RESPONSIVE_GRID)
+ .andSceneContainer()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index c287da8df135..407bdf8dcefb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -662,7 +662,7 @@ object TestShortcuts {
val standardAddShortcutRequest =
ShortcutCustomizationRequestInfo.Add(
label = "Standard shortcut",
- categoryType = ShortcutCategoryType.System,
+ categoryType = System,
subCategoryLabel = "Standard subcategory",
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index 2d05ee0fe234..755c218f6789 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -108,7 +108,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- assertThat(uiState).isEqualTo(ResetShortcutDialog())
+ assertThat(uiState).isEqualTo(ResetShortcutDialog)
}
}
@@ -118,45 +118,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- assertThat(uiState).isEqualTo(DeleteShortcutDialog())
- }
- }
-
- @Test
- fun uiState_consumedOnAddDialogShown() {
- testScope.runTest {
- val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
- viewModel.onDialogShown()
-
- assertThat((uiState as AddShortcutDialog).isDialogShowing)
- .isTrue()
- }
- }
-
- @Test
- fun uiState_consumedOnDeleteDialogShown() {
- testScope.runTest {
- val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
- viewModel.onDialogShown()
-
- assertThat(
- (uiState as DeleteShortcutDialog).isDialogShowing
- )
- .isTrue()
- }
- }
-
- @Test
- fun uiState_consumedOnResetDialogShown() {
- testScope.runTest {
- val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
- viewModel.onDialogShown()
-
- assertThat((uiState as ResetShortcutDialog).isDialogShowing)
- .isTrue()
+ assertThat(uiState).isEqualTo(DeleteShortcutDialog)
}
}
@@ -165,7 +127,6 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
- viewModel.onDialogShown()
viewModel.onDialogDismissed()
assertThat(uiState).isEqualTo(ShortcutCustomizationUiState.Inactive)
}
@@ -199,7 +160,6 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
- viewModel.onDialogShown()
assertThat((uiState as AddShortcutDialog).errorMessage)
.isEmpty()
@@ -339,7 +299,6 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
private suspend fun openAddShortcutDialogAndSetShortcut() {
viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
- viewModel.onDialogShown()
viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
@@ -349,14 +308,12 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
private suspend fun openDeleteShortcutDialogAndDeleteShortcut() {
viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
- viewModel.onDialogShown()
viewModel.deleteShortcutCurrentlyBeingCustomized()
}
private suspend fun openResetShortcutDialogAndResetAllCustomShortcuts() {
viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
- viewModel.onDialogShown()
viewModel.resetAllCustomShortcuts()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 3bd249689da9..78fce276a5f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -18,6 +18,13 @@ package com.android.systemui.keyboard.shortcut.ui.viewmodel
import android.app.role.RoleManager
import android.app.role.mockRoleManager
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.hardware.input.InputGestureData
+import android.hardware.input.fakeInputManager
import android.view.KeyEvent
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
@@ -31,6 +38,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
@@ -56,7 +64,6 @@ import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -64,6 +71,10 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -73,6 +84,9 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
private val fakeSystemSource = FakeKeyboardShortcutGroupsSource()
private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
private val fakeCurrentAppsSource = FakeKeyboardShortcutGroupsSource()
+ private val mockPackageManager: PackageManager = mock()
+ private val mockUserContext: Context = mock()
+ private val mockApplicationInfo: ApplicationInfo = mock()
private val kosmos =
Kosmos().also {
@@ -83,7 +97,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = fakeCurrentAppsSource
- it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { context })
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
private val testScope = kosmos.testScope
@@ -91,13 +105,20 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
private val sysUiState = kosmos.sysUiState
private val fakeUserTracker = kosmos.fakeUserTracker
private val mockRoleManager = kosmos.mockRoleManager
+ private val inputManager = kosmos.fakeInputManager.inputManager
private val viewModel = kosmos.shortcutHelperViewModel
+
@Before
fun setUp() {
fakeSystemSource.setGroups(TestShortcuts.systemGroups)
fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups)
fakeCurrentAppsSource.setGroups(TestShortcuts.currentAppGroups)
+ whenever(mockPackageManager.getApplicationInfo(anyString(), eq(0))).thenReturn(mockApplicationInfo)
+ whenever(mockPackageManager.getApplicationLabel(mockApplicationInfo)).thenReturn("Current App")
+ whenever(mockPackageManager.getApplicationIcon(anyString())).thenThrow(NameNotFoundException())
+ whenever(mockUserContext.packageManager).thenReturn(mockPackageManager)
+ whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
}
@Test
@@ -259,11 +280,11 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
fun shortcutsUiState_currentAppIsLauncher_defaultSelectedCategoryIsSystem() =
testScope.runTest {
whenever(
- mockRoleManager.getRoleHoldersAsUser(
- RoleManager.ROLE_HOME,
- fakeUserTracker.userHandle,
- )
+ mockRoleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_HOME,
+ fakeUserTracker.userHandle,
)
+ )
.thenReturn(listOf(TestShortcuts.currentAppPackageName))
val uiState by collectLastValue(viewModel.shortcutsUiState)
@@ -299,23 +320,23 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
label = "System",
iconSource = IconSource(imageVector = Icons.Default.Tv),
shortcutCategory =
- ShortcutCategory(
- System,
- subCategoryWithShortcutLabels("first Foo shortcut1"),
- subCategoryWithShortcutLabels(
- "second foO shortcut2",
- subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
- ),
+ ShortcutCategory(
+ System,
+ subCategoryWithShortcutLabels("first Foo shortcut1"),
+ subCategoryWithShortcutLabels(
+ "second foO shortcut2",
+ subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
),
+ ),
),
ShortcutCategoryUi(
label = "Multitasking",
iconSource = IconSource(imageVector = Icons.Default.VerticalSplit),
shortcutCategory =
- ShortcutCategory(
- MultiTasking,
- subCategoryWithShortcutLabels("third FoO shortcut1"),
- ),
+ ShortcutCategory(
+ MultiTasking,
+ subCategoryWithShortcutLabels("third FoO shortcut1"),
+ ),
),
)
}
@@ -387,6 +408,31 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
assertThat(activeUiState.defaultSelectedCategory).isInstanceOf(CurrentApp::class.java)
}
+ @Test
+ fun shortcutsUiState_shouldShowResetButton_isFalseWhenThereAreNoCustomShortcuts() =
+ testScope.runTest {
+ val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+ testHelper.showFromActivity()
+
+ val activeUiState = uiState as ShortcutsUiState.Active
+ assertThat(activeUiState.shouldShowResetButton).isFalse()
+ }
+
+ @Test
+ fun shortcutsUiState_shouldShowResetButton_isTrueWhenThereAreCustomShortcuts() =
+ testScope.runTest {
+ whenever(
+ inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ ).thenReturn(listOf(allAppsInputGestureData))
+ val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+ testHelper.showFromActivity()
+
+ val activeUiState = uiState as ShortcutsUiState.Active
+ assertThat(activeUiState.shouldShowResetButton).isTrue()
+ }
+
private fun groupWithShortcutLabels(
vararg shortcutLabels: String,
groupLabel: String = FIRST_SIMPLE_GROUP_LABEL,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 9f84e346d54a..f33de4d9144d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -128,7 +128,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
viewModel,
dialogManager,
wifiStateWorker,
- accessPointController
+ accessPointController,
)
underTest.initialize()
@@ -156,10 +156,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
testScope.runTest {
connectivityRepository.defaultConnections.value = DefaultConnectionModel()
wifiRepository.wifiScanResults.value =
- listOf(
- WifiScanEntry(ssid = "ssid 1"),
- WifiScanEntry(ssid = "ssid 2"),
- )
+ listOf(WifiScanEntry(ssid = "ssid 1"), WifiScanEntry(ssid = "ssid 2"))
runCurrent()
looper.processAllMessages()
@@ -204,10 +201,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
testScope.runTest {
airplaneModeRepository.setIsAirplaneMode(true)
connectivityRepository.defaultConnections.value =
- DefaultConnectionModel(
- wifi = Wifi(true),
- isValidated = true,
- )
+ DefaultConnectionModel(wifi = Wifi(true), isValidated = true)
wifiRepository.setIsWifiEnabled(true)
wifiRepository.setWifiNetwork(ACTIVE_WIFI)
@@ -222,10 +216,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
fun wifiConnected() =
testScope.runTest {
connectivityRepository.defaultConnections.value =
- DefaultConnectionModel(
- wifi = Wifi(true),
- isValidated = true,
- )
+ DefaultConnectionModel(wifi = Wifi(true), isValidated = true)
wifiRepository.setIsWifiEnabled(true)
wifiRepository.setWifiNetwork(ACTIVE_WIFI)
@@ -242,6 +233,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
whenever(wifiStateWorker.isWifiEnabled).thenReturn(true)
underTest.secondaryClick(null)
+ looper.processAllMessages()
verify(wifiStateWorker, times(1)).isWifiEnabled = eq(false)
}
@@ -251,6 +243,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
whenever(wifiStateWorker.isWifiEnabled).thenReturn(false)
underTest.secondaryClick(null)
+ looper.processAllMessages()
verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
}
@@ -258,10 +251,6 @@ class InternetTileNewImplTest : SysuiTestCase() {
companion object {
const val WIFI_SSID = "test ssid"
val ACTIVE_WIFI =
- WifiNetworkModel.Active.of(
- isValidated = true,
- level = 4,
- ssid = WIFI_SSID,
- )
+ WifiNetworkModel.Active.of(isValidated = true, level = 4, ssid = WIFI_SSID)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 0cf96047fcc0..b5ec0a022dd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -180,6 +180,7 @@ public class InternetTileTest extends SysuiTestCase {
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
mTile.secondaryClick(null);
+ mTestableLooper.processAllMessages();
verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(false));
}
@@ -189,6 +190,7 @@ public class InternetTileTest extends SysuiTestCase {
when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
mTile.secondaryClick(null);
+ mTestableLooper.processAllMessages();
verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(true));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 03c1f92aad4c..4068d9fd7f3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -46,6 +46,9 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.service.quickaccesswallet.Flags;
import android.service.quickaccesswallet.GetWalletCardsError;
import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -221,6 +224,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
}
@Test
+ @DisableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE})
public void testHandleClick_startQuickAccessUiIntent_noCard() {
setUpWalletCard(/* hasCard= */ false);
@@ -234,6 +238,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
}
@Test
+ @DisableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE})
public void testHandleClick_startQuickAccessUiIntent_hasCard() {
setUpWalletCard(/* hasCard= */ true);
@@ -247,6 +252,34 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
}
@Test
+ @EnableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE})
+ public void testHandleClick_startCardIntent_noCard() {
+ setUpWalletCard(/* hasCard= */ false);
+
+ mTile.handleClick(/* view= */ null);
+ mTestableLooper.processAllMessages();
+
+ verify(mController).startQuickAccessUiIntent(
+ eq(mActivityStarter),
+ eq(null),
+ /* hasCard= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE})
+ public void testHandleClick_startCardIntent_hasCard() {
+ setUpWalletCard(/* hasCard= */ true);
+
+ mTile.handleClick(null /* view */);
+ mTestableLooper.processAllMessages();
+
+ verify(mController).startWalletCardPendingIntent(
+ any(),
+ eq(mActivityStarter),
+ eq(null));
+ }
+
+ @Test
public void testHandleUpdateState_updateLabelAndIcon() {
QSTile.State state = new QSTile.State();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
deleted file mode 100644
index 3bd12e6efab6..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2018 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.phone;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.platform.test.annotations.DisableFlags;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeHeadsUpTracker;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
-import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper
-public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
-
- private final NotificationStackScrollLayoutController mStackScrollerController =
- mock(NotificationStackScrollLayoutController.class);
- private final ShadeViewController mShadeViewController =
- mock(ShadeViewController.class);
- private final ShadeHeadsUpTracker mShadeHeadsUpTracker = mock(ShadeHeadsUpTracker.class);
- private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
- private HeadsUpAppearanceController mHeadsUpAppearanceController;
- private NotificationTestHelper mTestHelper;
- private ExpandableNotificationRow mRow;
- private NotificationEntry mEntry;
- private HeadsUpStatusBarView mHeadsUpStatusBarView;
- private HeadsUpManager mHeadsUpManager;
- private View mOperatorNameView;
- private StatusBarStateController mStatusbarStateController;
- private PhoneStatusBarTransitions mPhoneStatusBarTransitions;
- private KeyguardBypassController mBypassController;
- private NotificationWakeUpCoordinator mWakeUpCoordinator;
- private KeyguardStateController mKeyguardStateController;
- private CommandQueue mCommandQueue;
- private NotificationRoundnessManager mNotificationRoundnessManager;
-
- @Before
- public void setUp() throws Exception {
- allowTestableLooperAsMainThread();
- mTestHelper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
- mRow = mTestHelper.createRow();
- mEntry = mRow.getEntry();
- mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
- mock(TextView.class));
- mHeadsUpManager = mock(HeadsUpManager.class);
- mOperatorNameView = new View(mContext);
- mStatusbarStateController = mock(StatusBarStateController.class);
- mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class);
- mBypassController = mock(KeyguardBypassController.class);
- mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
- mKeyguardStateController = mock(KeyguardStateController.class);
- mCommandQueue = mock(CommandQueue.class);
- mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
- when(mShadeViewController.getShadeHeadsUpTracker()).thenReturn(mShadeHeadsUpTracker);
- mHeadsUpAppearanceController = new HeadsUpAppearanceController(
- mHeadsUpManager,
- mStatusbarStateController,
- mPhoneStatusBarTransitions,
- mBypassController,
- mWakeUpCoordinator,
- mDarkIconDispatcher,
- mKeyguardStateController,
- mCommandQueue,
- mStackScrollerController,
- mShadeViewController,
- mNotificationRoundnessManager,
- mHeadsUpStatusBarView,
- new Clock(mContext, null),
- mock(HeadsUpNotificationIconInteractor.class),
- Optional.of(mOperatorNameView));
- mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
- }
-
- @Test
- public void testShowinEntryUpdated() {
- mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
- mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
- assertEquals(mRow.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
-
- mRow.setPinnedStatus(PinnedStatus.NotPinned);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- assertNull(mHeadsUpStatusBarView.getShowingEntry());
- }
-
- @Test
- public void testPinnedStatusUpdated() {
- mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
- mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
- assertEquals(PinnedStatus.PinnedBySystem, mHeadsUpAppearanceController.getPinnedStatus());
-
- mRow.setPinnedStatus(PinnedStatus.NotPinned);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- assertEquals(PinnedStatus.NotPinned, mHeadsUpAppearanceController.getPinnedStatus());
- }
-
- @Test
- @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
- public void testHeaderUpdated() {
- mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
- mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
- assertEquals(mRow.getHeaderVisibleAmount(), 0.0f, 0.0f);
-
- mRow.setPinnedStatus(PinnedStatus.NotPinned);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- assertEquals(mRow.getHeaderVisibleAmount(), 1.0f, 0.0f);
- }
-
- @Test
- public void testOperatorNameViewUpdated() {
- mHeadsUpAppearanceController.setAnimationsEnabled(false);
-
- mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
- mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
- assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
-
- mRow.setPinnedStatus(PinnedStatus.NotPinned);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
- }
-
- @Test
- public void constructor_animationValuesUpdated() {
- float appearFraction = .75f;
- float expandedHeight = 400f;
- when(mStackScrollerController.getAppearFraction()).thenReturn(appearFraction);
- when(mStackScrollerController.getExpandedHeight()).thenReturn(expandedHeight);
-
- HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
- mHeadsUpManager,
- mStatusbarStateController,
- mPhoneStatusBarTransitions,
- mBypassController,
- mWakeUpCoordinator,
- mDarkIconDispatcher,
- mKeyguardStateController,
- mCommandQueue,
- mStackScrollerController,
- mShadeViewController,
- mNotificationRoundnessManager,
- mHeadsUpStatusBarView,
- new Clock(mContext, null),
- mock(HeadsUpNotificationIconInteractor.class),
- Optional.empty());
-
- assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
- assertEquals(appearFraction, newController.mAppearFraction, 0.0f);
- }
-
- @Test
- public void testDestroy() {
- reset(mHeadsUpManager);
- reset(mDarkIconDispatcher);
- reset(mShadeHeadsUpTracker);
- reset(mStackScrollerController);
-
- mHeadsUpAppearanceController.onViewDetached();
-
- verify(mHeadsUpManager).removeListener(any());
- verify(mDarkIconDispatcher).removeDarkReceiver(any());
- verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any());
- verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(isNull());
- verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
- }
-
- @Test
- public void testPulsingRoundness_onUpdateHeadsUpAndPulsingRoundness() {
- // Pulsing: Enable flag and dozing
- when(mNotificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true);
- when(mTestHelper.getStatusBarStateController().isDozing()).thenReturn(true);
-
- // Pulsing: Enabled
- mRow.setHeadsUp(true);
- mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(mEntry);
-
- String debugString = mRow.getRoundableState().debugString();
- assertEquals(
- "If Pulsing is enabled, roundness should be set to 1. Value: " + debugString,
- /* expected = */ 1,
- /* actual = */ mRow.getTopRoundness(),
- /* delta = */ 0.001
- );
- assertTrue(debugString.contains("Pulsing"));
-
- // Pulsing: Disabled
- mRow.setHeadsUp(false);
- mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(mEntry);
-
- assertEquals(
- "If Pulsing is disabled, roundness should be set to 0. Value: "
- + mRow.getRoundableState().debugString(),
- /* expected = */ 0,
- /* actual = */ mRow.getTopRoundness(),
- /* delta = */ 0.001
- );
- }
-
- @Test
- public void testPulsingRoundness_onHeadsUpStateChanged() {
- // Pulsing: Enable flag and dozing
- when(mNotificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true);
- when(mTestHelper.getStatusBarStateController().isDozing()).thenReturn(true);
-
- // Pulsing: Enabled
- mEntry.setHeadsUp(true);
- mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, true);
-
- String debugString = mRow.getRoundableState().debugString();
- assertEquals(
- "If Pulsing is enabled, roundness should be set to 1. Value: " + debugString,
- /* expected = */ 1,
- /* actual = */ mRow.getTopRoundness(),
- /* delta = */ 0.001
- );
- assertTrue(debugString.contains("Pulsing"));
-
- // Pulsing: Disabled
- mEntry.setHeadsUp(false);
- mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, false);
-
- assertEquals(
- "If Pulsing is disabled, roundness should be set to 0. Value: "
- + mRow.getRoundableState().debugString(),
- /* expected = */ 0,
- /* actual = */ mRow.getTopRoundness(),
- /* delta = */ 0.001
- );
- }
-
- @Test
- public void onHeadsUpStateChanged_true_transitionsNotified() {
- mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, true);
-
- verify(mPhoneStatusBarTransitions).onHeadsUpStateChanged(true);
- }
-
- @Test
- public void onHeadsUpStateChanged_false_transitionsNotified() {
- mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, false);
-
- verify(mPhoneStatusBarTransitions).onHeadsUpStateChanged(false);
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
new file mode 100644
index 000000000000..d017402675bf
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 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.phone
+
+import android.platform.test.annotations.DisableFlags
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeHeadsUpTracker
+import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.HeadsUpStatusBarView
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class HeadsUpAppearanceControllerTest : SysuiTestCase() {
+ private val mStackScrollerController: NotificationStackScrollLayoutController =
+ mock<NotificationStackScrollLayoutController>()
+ private val mShadeViewController: ShadeViewController = mock<ShadeViewController>()
+ private val mShadeHeadsUpTracker: ShadeHeadsUpTracker = mock<ShadeHeadsUpTracker>()
+ private val mDarkIconDispatcher: DarkIconDispatcher = mock<DarkIconDispatcher>()
+ private var mHeadsUpAppearanceController: HeadsUpAppearanceController? = null
+ private var mTestHelper: NotificationTestHelper? = null
+ private var mRow: ExpandableNotificationRow? = null
+ private var mEntry: NotificationEntry? = null
+ private var mHeadsUpStatusBarView: HeadsUpStatusBarView? = null
+ private var mHeadsUpManager: HeadsUpManager? = null
+ private var mOperatorNameView: View? = null
+ private var mStatusbarStateController: StatusBarStateController? = null
+ private var mPhoneStatusBarTransitions: PhoneStatusBarTransitions? = null
+ private var mBypassController: KeyguardBypassController? = null
+ private var mWakeUpCoordinator: NotificationWakeUpCoordinator? = null
+ private var mKeyguardStateController: KeyguardStateController? = null
+ private var mCommandQueue: CommandQueue? = null
+ private var mNotificationRoundnessManager: NotificationRoundnessManager? = null
+
+ @Before
+ @Throws(Exception::class)
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ mTestHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ mRow = mTestHelper!!.createRow()
+ mEntry = mRow!!.entry
+ mHeadsUpStatusBarView = HeadsUpStatusBarView(mContext, mock<View>(), mock<TextView>())
+ mHeadsUpManager = mock<HeadsUpManager>()
+ mOperatorNameView = View(mContext)
+ mStatusbarStateController = mock<StatusBarStateController>()
+ mPhoneStatusBarTransitions = mock<PhoneStatusBarTransitions>()
+ mBypassController = mock<KeyguardBypassController>()
+ mWakeUpCoordinator = mock<NotificationWakeUpCoordinator>()
+ mKeyguardStateController = mock<KeyguardStateController>()
+ mCommandQueue = mock<CommandQueue>()
+ mNotificationRoundnessManager = mock<NotificationRoundnessManager>()
+ whenever(mShadeViewController.shadeHeadsUpTracker).thenReturn(mShadeHeadsUpTracker)
+ mHeadsUpAppearanceController =
+ HeadsUpAppearanceController(
+ mHeadsUpManager,
+ mStatusbarStateController,
+ mPhoneStatusBarTransitions,
+ mBypassController,
+ mWakeUpCoordinator,
+ mDarkIconDispatcher,
+ mKeyguardStateController,
+ mCommandQueue,
+ mStackScrollerController,
+ mShadeViewController,
+ mNotificationRoundnessManager,
+ mHeadsUpStatusBarView,
+ Clock(mContext, null),
+ mock<HeadsUpNotificationIconInteractor>(),
+ Optional.of(mOperatorNameView!!),
+ )
+ mHeadsUpAppearanceController!!.setAppearFraction(0.0f, 0.0f)
+ }
+
+ @Test
+ fun testShowinEntryUpdated() {
+ mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+ mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+ assertThat(mHeadsUpStatusBarView!!.showingEntry).isEqualTo(mRow!!.entry)
+
+ mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+ mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+ assertThat(mHeadsUpStatusBarView!!.showingEntry).isNull()
+ }
+
+ @Test
+ fun testPinnedStatusUpdated() {
+ mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+ mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+ assertThat(mHeadsUpAppearanceController!!.pinnedStatus)
+ .isEqualTo(PinnedStatus.PinnedBySystem)
+
+ mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+ mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+ assertThat(mHeadsUpAppearanceController!!.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
+ }
+
+ @Test
+ @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
+ fun testHeaderUpdated() {
+ mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+ mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+ assertThat(mRow!!.headerVisibleAmount).isEqualTo(0.0f)
+
+ mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+ mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+ assertThat(mRow!!.headerVisibleAmount).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun testOperatorNameViewUpdated() {
+ mHeadsUpAppearanceController!!.setAnimationsEnabled(false)
+
+ mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+ mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+ assertThat(mOperatorNameView!!.visibility).isEqualTo(View.INVISIBLE)
+
+ mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+ mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+ assertThat(mOperatorNameView!!.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun constructor_animationValuesUpdated() {
+ val appearFraction = .75f
+ val expandedHeight = 400f
+ whenever(mStackScrollerController.appearFraction).thenReturn(appearFraction)
+ whenever(mStackScrollerController.expandedHeight).thenReturn(expandedHeight)
+
+ val newController =
+ HeadsUpAppearanceController(
+ mHeadsUpManager,
+ mStatusbarStateController,
+ mPhoneStatusBarTransitions,
+ mBypassController,
+ mWakeUpCoordinator,
+ mDarkIconDispatcher,
+ mKeyguardStateController,
+ mCommandQueue,
+ mStackScrollerController,
+ mShadeViewController,
+ mNotificationRoundnessManager,
+ mHeadsUpStatusBarView,
+ Clock(mContext, null),
+ mock<HeadsUpNotificationIconInteractor>(),
+ Optional.empty(),
+ )
+
+ assertThat(newController.mExpandedHeight).isEqualTo(expandedHeight)
+ assertThat(newController.mAppearFraction).isEqualTo(appearFraction)
+ }
+
+ @Test
+ fun testDestroy() {
+ reset(mHeadsUpManager)
+ reset(mDarkIconDispatcher)
+ reset(mShadeHeadsUpTracker)
+ reset(mStackScrollerController)
+
+ mHeadsUpAppearanceController!!.onViewDetached()
+
+ verify(mHeadsUpManager!!).removeListener(any())
+ verify(mDarkIconDispatcher).removeDarkReceiver(any())
+ verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any())
+ verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(null)
+ verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any())
+ }
+
+ @Test
+ fun testPulsingRoundness_onUpdateHeadsUpAndPulsingRoundness() {
+ // Pulsing: Enable flag and dozing
+ whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
+ whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+
+ // Pulsing: Enabled
+ mRow!!.isHeadsUp = true
+ mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+
+ val debugString: String = mRow!!.roundableState.debugString()
+ // If Pulsing is enabled, roundness should be set to 1
+ assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+ assertThat(debugString).contains("Pulsing")
+
+ // Pulsing: Disabled
+ mRow!!.isHeadsUp = false
+ mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+
+ // If Pulsing is disabled, roundness should be set to 0
+ assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+ }
+
+ @Test
+ fun testPulsingRoundness_onHeadsUpStateChanged() {
+ // Pulsing: Enable flag and dozing
+ whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
+ whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+
+ // Pulsing: Enabled
+ mEntry!!.setHeadsUp(true)
+ mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+
+ val debugString: String = mRow!!.roundableState.debugString()
+ // If Pulsing is enabled, roundness should be set to 1
+ assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+ assertThat(debugString).contains("Pulsing")
+
+ // Pulsing: Disabled
+ mEntry!!.setHeadsUp(false)
+ mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+
+ // If Pulsing is disabled, roundness should be set to 0
+ assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+ }
+
+ @Test
+ fun onHeadsUpStateChanged_true_transitionsNotified() {
+ mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+
+ verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(true)
+ }
+
+ @Test
+ fun onHeadsUpStateChanged_false_transitionsNotified() {
+ mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+
+ verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(false)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
index 110dec6c33aa..f76ee5e3ebc9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
@@ -87,7 +87,7 @@ private const val PROC_STATE_INVISIBLE = ActivityManager.PROCESS_STATE_FOREGROUN
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
+@DisableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP, StatusBarChipsModernization.FLAG_NAME)
class OngoingCallControllerViaListenerTest : SysuiTestCase() {
private val kosmos = Kosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
index 2ad50cc38b7c..647b5f86fcee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
@@ -29,6 +29,7 @@ import android.widget.LinearLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
import com.android.systemui.SysuiTestCase
@@ -77,6 +78,7 @@ import org.mockito.kotlin.whenever
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
@EnableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
+@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
class OngoingCallControllerViaRepoTest : SysuiTestCase() {
private val kosmos = Kosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index 4c6eaa589e6a..a44631348796 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.phone.ongoingcall.data.repository
+import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
import com.google.common.truth.Truth.assertThat
@@ -28,6 +31,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
+@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
class OngoingCallRepositoryTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val underTest = kosmos.ongoingCallRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
new file mode 100644
index 000000000000..ca1413e48966
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor
+
+import android.app.PendingIntent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.CallType
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OngoingCallInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos().useUnconfinedTestDispatcher()
+ private val repository = kosmos.activeNotificationListRepository
+ private val underTest = kosmos.ongoingCallInteractor
+
+ @Test
+ fun noNotification_emitsNoCall() = runTest {
+ val state by collectLastValue(underTest.ongoingCallState)
+ assertThat(state).isInstanceOf(OngoingCallModel.NoCall::class.java)
+ }
+
+ @Test
+ fun ongoingCallNotification_setsNotificationIconAndIntent() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.ongoingCallState)
+
+ // Set up notification with icon view and intent
+ val testIconView: StatusBarIconView = mock()
+ val testIntent: PendingIntent = mock()
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
+ statusBarChipIcon = testIconView,
+ contentIntent = testIntent
+ )
+ )
+ }
+ .build()
+
+ // Verify model is InCall and has the correct icon and intent.
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+ val model = latest as OngoingCallModel.InCall
+ assertThat(model.notificationIconView).isSameInstanceAs(testIconView)
+ assertThat(model.intent).isSameInstanceAs(testIntent)
+ }
+
+ @Test
+ fun ongoingCallNotification_emitsInCall() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.ongoingCallState)
+
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+ )
+ )
+ }
+ .build()
+
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+ }
+
+ @Test
+ fun notificationRemoved_emitsNoCall() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.ongoingCallState)
+
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+ )
+ )
+ }
+ .build()
+
+ repository.activeNotifications.value = ActiveNotificationsStore()
+ assertThat(latest).isInstanceOf(OngoingCallModel.NoCall::class.java)
+ }
+
+ @Test
+ fun ongoingCallNotification_appVisibleInitially_emitsInCallWithVisibleApp() =
+ kosmos.runTest {
+ kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = true
+ val latest by collectLastValue(underTest.ongoingCallState)
+
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
+ uid = UID
+ )
+ )
+ }
+ .build()
+
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+ }
+
+ @Test
+ fun ongoingCallNotification_appNotVisibleInitially_emitsInCall() =
+ kosmos.runTest {
+ kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
+ val latest by collectLastValue(underTest.ongoingCallState)
+
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
+ uid = UID
+ )
+ )
+ }
+ .build()
+
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+ }
+
+ @Test
+ fun ongoingCallNotification_visibilityChanges_updatesState() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.ongoingCallState)
+
+ // Start with notification and app not visible
+ kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
+ repository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ whenTime = 1000L,
+ callType = CallType.Ongoing,
+ uid = UID
+ )
+ )
+ }
+ .build()
+ assertThat(latest)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
+
+ // App becomes visible
+ kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+
+ // App becomes invisible again
+ kosmos.activityManagerRepository.fake.setIsAppVisible(UID, false)
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+ }
+
+ companion object {
+ private const val UID = 885
+ }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bc81a4b6bb67..e417da4d8815 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3814,6 +3814,9 @@
shortcut helper The helper is a component that shows the user which keyboard shortcuts
they can use. [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_customize_button_text">Customize</string>
+ <!-- Description text of the button that allows user to resets all custom shortcuts in keyboard
+ shortcut helper when in customization mode. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_reset_button_text">Reset</string>
<!-- Description text of the button that allows user to exit shortcut customization mode in
keyboard shortcut helper The helper is a component that shows the user which keyboard
shortcuts they can use. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json
new file mode 100644
index 000000000000..c5a83c4d0d8c
--- /dev/null
+++ b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json
@@ -0,0 +1,95 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 5,
+ "identityHash": "a83f96ef4babe730b3a00e8acb777a25",
+ "entities": [
+ {
+ "tableName": "communal_widget_table",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL, `user_serial_number` INTEGER NOT NULL DEFAULT -1, `span_y` INTEGER NOT NULL DEFAULT 3, `span_y_new` INTEGER NOT NULL DEFAULT 1)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "widgetId",
+ "columnName": "widget_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "componentName",
+ "columnName": "component_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "itemId",
+ "columnName": "item_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userSerialNumber",
+ "columnName": "user_serial_number",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "spanY",
+ "columnName": "span_y",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "3"
+ },
+ {
+ "fieldPath": "spanYNew",
+ "columnName": "span_y_new",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "uid"
+ ]
+ }
+ },
+ {
+ "tableName": "communal_item_rank_table",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rank",
+ "columnName": "rank",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "uid"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a83f96ef4babe730b3a00e8acb777a25')"
+ ]
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 071cf8a46b9f..5af80cbd4b29 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -63,6 +63,7 @@ import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.plugins.clocks.ZenData.ZenMode
import com.android.systemui.res.R as SysuiR
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.statusbar.policy.BatteryController
@@ -466,6 +467,15 @@ constructor(
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
zenModeController.addCallback(zenModeCallback)
+ if (SceneContainerFlag.isEnabled) {
+ handleDoze(
+ when (AOD) {
+ keyguardTransitionInteractor.getCurrentState() -> 1f
+ keyguardTransitionInteractor.getStartedState() -> 1f
+ else -> 0f
+ }
+ )
+ }
disposableHandle =
parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt
index c3d2683ce953..41ea7b6c2202 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt
@@ -29,9 +29,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
/** Utilities for communal backup and restore. */
-class CommunalBackupUtils(
- private val context: Context,
-) {
+class CommunalBackupUtils(private val context: Context) {
/**
* Retrieves a communal hub state protobuf that represents the current state of the communal
@@ -50,6 +48,8 @@ class CommunalBackupUtils(
widgetId = widget.widgetId
componentName = widget.componentName
userSerialNumber = widget.userSerialNumber
+ spanY = widget.spanY
+ spanYNew = widget.spanYNew
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
index e72088f37fa7..679d0714b1b4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
@@ -26,9 +26,11 @@ import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl
+import com.android.systemui.communal.shared.model.SpanValue
+import com.android.systemui.communal.shared.model.toResponsive
import com.android.systemui.res.R
-@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 4)
+@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 5)
abstract class CommunalDatabase : RoomDatabase() {
abstract fun communalWidgetDao(): CommunalWidgetDao
@@ -59,7 +61,12 @@ abstract class CommunalDatabase : RoomDatabase() {
context.resources.getString(R.string.config_communalDatabase),
)
.also { builder ->
- builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
+ builder.addMigrations(
+ MIGRATION_1_2,
+ MIGRATION_2_3,
+ MIGRATION_3_4,
+ MIGRATION_4_5,
+ )
builder.fallbackToDestructiveMigration(dropAllTables = true)
callback?.let { callback -> builder.addCallback(callback) }
}
@@ -123,5 +130,30 @@ abstract class CommunalDatabase : RoomDatabase() {
)
}
}
+
+ /** This migration adds a new spanY column for responsive grid sizing. */
+ @VisibleForTesting
+ val MIGRATION_4_5 =
+ object : Migration(4, 5) {
+ override fun migrate(db: SupportSQLiteDatabase) {
+ Log.i(TAG, "Migrating from version 4 to 5")
+ db.execSQL(
+ "ALTER TABLE communal_widget_table " +
+ "ADD COLUMN span_y_new INTEGER NOT NULL DEFAULT 1"
+ )
+ db.query("SELECT item_id, span_y FROM communal_widget_table").use { cursor ->
+ while (cursor.moveToNext()) {
+ val id = cursor.getInt(cursor.getColumnIndex("item_id"))
+ val spanYFixed =
+ SpanValue.Fixed(cursor.getInt(cursor.getColumnIndex("span_y")))
+ val spanYResponsive = spanYFixed.toResponsive()
+ db.execSQL(
+ "UPDATE communal_widget_table SET span_y_new = " +
+ "${spanYResponsive.value} WHERE item_id = $id"
+ )
+ }
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
index f9d2a843c213..6ef4bb8e55eb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
@@ -45,7 +45,12 @@ data class CommunalWidgetItem(
* The vertical span of the widget. Span_Y default value corresponds to
* CommunalContentSize.HALF.span
*/
- @ColumnInfo(name = "span_y", defaultValue = "3") val spanY: Int,
+ @Deprecated("Use spanYNew instead")
+ @ColumnInfo(name = "span_y", defaultValue = "3")
+ val spanY: Int,
+
+ /** The vertical span of the widget in grid cell units. */
+ @ColumnInfo(name = "span_y_new", defaultValue = "1") val spanYNew: Int,
) {
companion object {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 3d40aa75b488..3907a37cd5d9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -27,7 +27,9 @@ import androidx.room.Update
import androidx.sqlite.db.SupportSQLiteDatabase
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.communal.nano.CommunalHubState
-import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.SpanValue
+import com.android.systemui.communal.shared.model.toFixed
+import com.android.systemui.communal.shared.model.toResponsive
import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.communal.widgets.CommunalWidgetModule.Companion.DEFAULT_WIDGETS
import com.android.systemui.dagger.SysUISingleton
@@ -101,6 +103,7 @@ constructor(
componentName = name,
rank = index,
userSerialNumber = userSerialNumber,
+ spanY = SpanValue.Fixed(3),
)
}
}
@@ -155,15 +158,16 @@ interface CommunalWidgetDao {
@Query(
"INSERT INTO communal_widget_table" +
- "(widget_id, component_name, item_id, user_serial_number, span_y) " +
- "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber, :spanY)"
+ "(widget_id, component_name, item_id, user_serial_number, span_y, span_y_new) " +
+ "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber, :spanY, :spanYNew)"
)
fun insertWidget(
widgetId: Int,
componentName: String,
itemId: Long,
userSerialNumber: Int,
- spanY: Int = 3,
+ spanY: Int,
+ spanYNew: Int,
): Long
@Query("INSERT INTO communal_item_rank_table(rank) VALUES(:rank)")
@@ -189,10 +193,12 @@ interface CommunalWidgetDao {
}
@Transaction
- fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+ fun resizeWidget(appWidgetId: Int, spanY: SpanValue, widgetIdToRankMap: Map<Int, Int>) {
val widget = getWidgetByIdNow(appWidgetId)
if (widget != null) {
- updateWidget(widget.copy(spanY = spanY))
+ updateWidget(
+ widget.copy(spanY = spanY.toFixed().value, spanYNew = spanY.toResponsive().value)
+ )
}
updateWidgetOrder(widgetIdToRankMap)
}
@@ -203,7 +209,7 @@ interface CommunalWidgetDao {
provider: ComponentName,
rank: Int? = null,
userSerialNumber: Int,
- spanY: Int = CommunalContentSize.HALF.span,
+ spanY: SpanValue,
): Long {
return addWidget(
widgetId = widgetId,
@@ -220,7 +226,7 @@ interface CommunalWidgetDao {
componentName: String,
rank: Int? = null,
userSerialNumber: Int,
- spanY: Int = 3,
+ spanY: SpanValue,
): Long {
val widgets = getWidgetsNow()
@@ -241,7 +247,8 @@ interface CommunalWidgetDao {
componentName = componentName,
itemId = insertItemRank(newRank),
userSerialNumber = userSerialNumber,
- spanY = spanY,
+ spanY = spanY.toFixed().value,
+ spanYNew = spanY.toResponsive().value,
)
}
@@ -264,7 +271,11 @@ interface CommunalWidgetDao {
clearCommunalItemRankTable()
state.widgets.forEach {
- val spanY = if (it.spanY != 0) it.spanY else CommunalContentSize.HALF.span
+ // Check if there is a new value to restore. If so, restore that new value.
+ val spanYResponsive = if (it.spanYNew != 0) SpanValue.Responsive(it.spanYNew) else null
+ // If no new value, restore any existing old values.
+ val spanY = spanYResponsive ?: SpanValue.Fixed(it.spanY.coerceIn(3, 6))
+
addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber, spanY)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 29569f8b7df5..e44d78baeb35 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -22,6 +22,7 @@ import android.content.ComponentName
import android.os.UserHandle
import android.os.UserManager
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.shared.model.PackageInstallSession
@@ -33,6 +34,7 @@ import com.android.systemui.communal.data.db.DefaultWidgetPopulation.SkipReason.
import com.android.systemui.communal.nano.CommunalHubState
import com.android.systemui.communal.proto.toCommunalHubState
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.SpanValue
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -143,15 +145,21 @@ constructor(
componentName = widget.componentName,
rank = rank.rank,
providerInfo = providers[widget.widgetId],
- spanY = widget.spanY,
+ spanY = if (communalResponsiveGrid()) widget.spanYNew else widget.spanY,
)
}
}
override fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
if (!communalWidgetResizing()) return
+ val spanValue =
+ if (communalResponsiveGrid()) {
+ SpanValue.Responsive(spanY)
+ } else {
+ SpanValue.Fixed(spanY)
+ }
bgScope.launch {
- communalWidgetDao.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+ communalWidgetDao.resizeWidget(appWidgetId, spanValue, widgetIdToRankMap)
logger.i({ "Updated spanY of widget $int1 to $int2." }) {
int1 = appWidgetId
int2 = spanY
@@ -225,7 +233,7 @@ constructor(
provider = provider,
rank = rank,
userSerialNumber = userManager.getUserSerialNumber(user.identifier),
- spanY = 3,
+ spanY = SpanValue.Fixed(3),
)
backupManager.dataChanged()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 602fe307a1fe..f9b30c6c2ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -34,9 +34,9 @@ import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.shared.model.CommunalContentSize.FULL
-import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
-import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
+import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.FULL
+import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.HALF
+import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.THIRD
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.EditModeState
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 30f580e472e8..da613f58dce8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -22,6 +22,7 @@ import android.content.ComponentName
import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.widget.RemoteViews
+import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.communal.shared.model.CommunalContentSize
import java.util.UUID
@@ -35,7 +36,7 @@ sealed interface CommunalContentModel {
/** The minimum size content can be resized to. */
val minSize: CommunalContentSize
- get() = CommunalContentSize.HALF
+ get() = fixedHalfOrResponsiveSize()
/**
* A type of communal content is ongoing / live / ephemeral, and can be sized and ordered
@@ -44,7 +45,12 @@ sealed interface CommunalContentModel {
sealed interface Ongoing : CommunalContentModel {
override var size: CommunalContentSize
override val minSize
- get() = CommunalContentSize.THIRD
+ get() =
+ if (communalResponsiveGrid()) {
+ CommunalContentSize.Responsive(1)
+ } else {
+ CommunalContentSize.FixedSize.THIRD
+ }
/** Timestamp in milliseconds of when the content was created. */
val createdTimestampMillis: Long
@@ -100,14 +106,16 @@ sealed interface CommunalContentModel {
class WidgetPlaceholder : CommunalContentModel {
override val key: String = KEY.widgetPlaceholder()
// Same as widget size.
- override val size = CommunalContentSize.HALF
+ override val size: CommunalContentSize
+ get() = fixedHalfOrResponsiveSize()
}
/** A CTA tile in the glanceable hub view mode which can be dismissed. */
class CtaTileInViewMode : CommunalContentModel {
override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY
// Same as widget size.
- override val size = CommunalContentSize.HALF
+ override val size: CommunalContentSize
+ get() = fixedHalfOrResponsiveSize()
}
class Tutorial(id: Int, override var size: CommunalContentSize) : CommunalContentModel {
@@ -118,15 +126,15 @@ sealed interface CommunalContentModel {
smartspaceTargetId: String,
val remoteViews: RemoteViews,
override val createdTimestampMillis: Long,
- override var size: CommunalContentSize = CommunalContentSize.HALF,
+ override var size: CommunalContentSize = fixedHalfOrResponsiveSize(),
) : Ongoing {
override val key = KEY.smartspace(smartspaceTargetId)
}
class Umo(
override val createdTimestampMillis: Long,
- override var size: CommunalContentSize = CommunalContentSize.HALF,
- override var minSize: CommunalContentSize = CommunalContentSize.HALF,
+ override var size: CommunalContentSize = fixedHalfOrResponsiveSize(),
+ override var minSize: CommunalContentSize = fixedHalfOrResponsiveSize(),
) : Ongoing {
override val key = KEY.umo()
}
@@ -170,3 +178,10 @@ sealed interface CommunalContentModel {
fun isLiveContent() = this is Smartspace || this is Umo
}
+
+private fun fixedHalfOrResponsiveSize() =
+ if (communalResponsiveGrid()) {
+ CommunalContentSize.Responsive(1)
+ } else {
+ CommunalContentSize.FixedSize.HALF
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto b/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto
index 7602a7afce4e..04717d0c864b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto
+++ b/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto
@@ -39,7 +39,10 @@ message CommunalHubState {
// Serial number of the user associated with the widget.
int32 user_serial_number = 4;
- // The vertical span of the widget
+ // The vertical span of the widget, replaced by span_y_new.
int32 span_y = 5;
+
+ // The vertical span of the widget.
+ int32 span_y_new = 6;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index cf80b7d62fd5..df30716ebaf2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -16,27 +16,39 @@
package com.android.systemui.communal.shared.model
+import com.android.systemui.Flags.communalResponsiveGrid
+
/**
* Supported sizes for communal content in the layout grid.
*
- * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents
- * HALF, 2 represents THIRD, and 1 represents SIXTH.
+ * @property span The span of the content in a column.
*/
-enum class CommunalContentSize(val span: Int) {
- /** Content takes the full height of the column. */
- FULL(6),
+sealed interface CommunalContentSize {
+ val span: Int
+
+ @Deprecated("Use Responsive size instead")
+ enum class FixedSize(override val span: Int) : CommunalContentSize {
+ /** Content takes the full height of the column. */
+ FULL(6),
- /** Content takes half of the height of the column. */
- HALF(3),
+ /** Content takes half of the height of the column. */
+ HALF(3),
+
+ /** Content takes a third of the height of the column. */
+ THIRD(2),
+ }
- /** Content takes a third of the height of the column. */
- THIRD(2);
+ @JvmInline value class Responsive(override val span: Int) : CommunalContentSize
companion object {
/** Converts from span to communal content size. */
fun toSize(span: Int): CommunalContentSize {
- return entries.find { it.span == span }
- ?: throw IllegalArgumentException("$span is not a valid span size")
+ return if (communalResponsiveGrid()) {
+ Responsive(span)
+ } else {
+ FixedSize.entries.find { it.span == span }
+ ?: throw IllegalArgumentException("$span is not a valid span size")
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt
new file mode 100644
index 000000000000..15cc6b0ad2c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.communal.shared.model
+
+/** Models possible span values for different grid formats. */
+sealed interface SpanValue {
+ val value: Int
+
+ @Deprecated("Use Responsive sizes instead")
+ @JvmInline
+ value class Fixed(override val value: Int) : SpanValue
+
+ @JvmInline value class Responsive(override val value: Int) : SpanValue
+}
+
+fun SpanValue.toResponsive(): SpanValue.Responsive =
+ when (this) {
+ is SpanValue.Responsive -> this
+ is SpanValue.Fixed -> SpanValue.Responsive((this.value / 3).coerceAtMost(1))
+ }
+
+fun SpanValue.toFixed(): SpanValue.Fixed =
+ when (this) {
+ is SpanValue.Fixed -> this
+ is SpanValue.Responsive -> SpanValue.Fixed((this.value * 3).coerceIn(3, 6))
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
index 14016783a478..37433ca4faf3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
@@ -16,7 +16,9 @@
package com.android.systemui.keyboard.shortcut.shared.model
-data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>)
+data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>) {
+ val containsCustomShortcuts: Boolean = shortcuts.any { it.containsCustomShortcutCommands }
+}
class ShortcutSubCategoryBuilder(val label: String) {
private val shortcuts = mutableListOf<Shortcut>()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index bd0430bf96c3..274fa59045d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -53,15 +53,18 @@ constructor(
override suspend fun onActivated(): Nothing {
viewModel.shortcutCustomizationUiState.collect { uiState ->
- val shouldShowAddDialog = uiState is AddShortcutDialog && !uiState.isDialogShowing
- val shouldShowDeleteDialog = uiState is DeleteShortcutDialog && !uiState.isDialogShowing
- val shouldShowResetDialog = uiState is ResetShortcutDialog && !uiState.isDialogShowing
- if (shouldShowDeleteDialog || shouldShowAddDialog || shouldShowResetDialog) {
- dialog = createDialog().also { it.show() }
- viewModel.onDialogShown()
- } else if (uiState is ShortcutCustomizationUiState.Inactive) {
- dialog?.dismiss()
- dialog = null
+ when(uiState){
+ is AddShortcutDialog,
+ is DeleteShortcutDialog,
+ is ResetShortcutDialog -> {
+ if (dialog == null){
+ dialog = createDialog().also { it.show() }
+ }
+ }
+ is ShortcutCustomizationUiState.Inactive -> {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 79293077bc4c..2fdcf8767ae5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyboard.shortcut.ui.composable
import android.graphics.drawable.Icon
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -56,6 +57,7 @@ import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.DeleteOutline
import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material3.CenterAlignedTopAppBar
@@ -113,7 +115,6 @@ import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
@@ -125,6 +126,7 @@ import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
import com.android.systemui.res.R
import kotlinx.coroutines.delay
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
@Composable
fun ShortcutHelper(
@@ -187,6 +189,7 @@ private fun ActiveShortcutHelper(
onKeyboardSettingsClicked,
shortcutsUiState.isShortcutCustomizerFlagEnabled,
onCustomizationRequested,
+ shortcutsUiState.shouldShowResetButton
)
}
}
@@ -377,6 +380,7 @@ private fun ShortcutHelperTwoPane(
onKeyboardSettingsClicked: () -> Unit,
isShortcutCustomizerFlagEnabled: Boolean,
onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+ shouldShowResetButton: Boolean
) {
val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
var isCustomizing by remember { mutableStateOf(false) }
@@ -389,11 +393,14 @@ private fun ShortcutHelperTwoPane(
TitleBar(isCustomizing)
}
if (isShortcutCustomizerFlagEnabled) {
- if (isCustomizing) {
- DoneButton(onClick = { isCustomizing = false })
- } else {
- CustomizeButton(onClick = { isCustomizing = true })
- }
+ CustomizationButtonsContainer(
+ isCustomizing = isCustomizing,
+ onToggleCustomizationMode = { isCustomizing = !isCustomizing },
+ onReset = {
+ onCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+ },
+ shouldShowResetButton = shouldShowResetButton,
+ )
} else {
Spacer(modifier = Modifier.width(if (isCustomizing) 69.dp else 133.dp))
}
@@ -422,6 +429,38 @@ private fun ShortcutHelperTwoPane(
}
@Composable
+private fun CustomizationButtonsContainer(
+ isCustomizing: Boolean,
+ shouldShowResetButton: Boolean,
+ onToggleCustomizationMode: () -> Unit,
+ onReset: () -> Unit,
+) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ if (isCustomizing) {
+ if (shouldShowResetButton) {
+ ResetButton(onClick = onReset)
+ }
+ DoneButton(onClick = onToggleCustomizationMode)
+ } else {
+ CustomizeButton(onClick = onToggleCustomizationMode)
+ }
+ }
+}
+
+@Composable
+private fun ResetButton(onClick: () -> Unit) {
+ ShortcutHelperButton(
+ onClick = onClick,
+ color = Color.Transparent,
+ width = 99.dp,
+ iconSource = IconSource(imageVector = Icons.Default.Refresh),
+ text = stringResource(id = R.string.shortcut_helper_reset_button_text),
+ contentColor = MaterialTheme.colorScheme.primary,
+ border = BorderStroke(color = MaterialTheme.colorScheme.outlineVariant, width = 1.dp),
+ )
+}
+
+@Composable
private fun CustomizeButton(onClick: () -> Unit) {
ShortcutHelperButton(
onClick = onClick,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index 58ce194694df..55c0fe297bcb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -228,74 +228,8 @@ fun ShortcutHelperButton(
contentColor: Color,
contentPaddingHorizontal: Dp = 16.dp,
contentPaddingVertical: Dp = 10.dp,
-) {
- ClickableShortcutSurface(
- onClick = onClick,
- shape = shape,
- color = color,
- modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
- interactionsConfig =
- InteractionsConfig(
- hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
- hoverOverlayAlpha = 0.11f,
- pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
- pressedOverlayAlpha = 0.15f,
- focusOutlineColor = MaterialTheme.colorScheme.secondary,
- focusOutlineStrokeWidth = 3.dp,
- focusOutlinePadding = 2.dp,
- surfaceCornerRadius = 28.dp,
- focusOutlineCornerRadius = 33.dp,
- ),
- ) {
- Row(
- modifier =
- Modifier.padding(
- horizontal = contentPaddingHorizontal,
- vertical = contentPaddingVertical,
- ),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center,
- ) {
- if (iconSource.imageVector != null) {
- Icon(
- tint = contentColor,
- imageVector = iconSource.imageVector,
- contentDescription = null,
- modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center),
- )
- }
-
- if (iconSource.imageVector != null && text != null) {
- Spacer(modifier = Modifier.weight(1f))
- }
-
- if (text != null) {
- Text(
- text,
- color = contentColor,
- fontSize = 14.sp,
- style = MaterialTheme.typography.labelLarge,
- modifier = Modifier.wrapContentSize(Alignment.Center),
- )
- }
- }
- }
-}
-
-@Composable
-fun ShortcutHelperButton(
- modifier: Modifier = Modifier,
- onClick: () -> Unit,
- shape: Shape = RoundedCornerShape(360.dp),
- color: Color,
- width: Dp,
- height: Dp = 40.dp,
- iconSource: IconSource = IconSource(),
- text: String? = null,
- contentColor: Color,
- contentPaddingHorizontal: Dp = 16.dp,
- contentPaddingVertical: Dp = 10.dp,
enabled: Boolean = true,
+ border: BorderStroke? = null,
) {
ShortcutHelperButtonSurface(
onClick = onClick,
@@ -305,6 +239,7 @@ fun ShortcutHelperButton(
enabled = enabled,
width = width,
height = height,
+ border = border,
) {
Row(
modifier =
@@ -342,7 +277,7 @@ fun ShortcutHelperButton(
}
@Composable
-fun ShortcutHelperButtonSurface(
+private fun ShortcutHelperButtonSurface(
onClick: () -> Unit,
shape: Shape,
color: Color,
@@ -350,6 +285,7 @@ fun ShortcutHelperButtonSurface(
enabled: Boolean,
width: Dp,
height: Dp,
+ border: BorderStroke?,
content: @Composable () -> Unit,
) {
if (enabled) {
@@ -357,6 +293,7 @@ fun ShortcutHelperButtonSurface(
onClick = onClick,
shape = shape,
color = color,
+ border = border,
modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
interactionsConfig =
InteractionsConfig(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt
index f5d478b5855d..27287721b333 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt
@@ -31,4 +31,6 @@ data class ShortcutCategoryUi(
iconSource: IconSource,
shortcutCategory: ShortcutCategory,
) : this(label, iconSource, shortcutCategory.type, shortcutCategory.subCategories)
+
+ val containsCustomShortcuts: Boolean = subCategories.any { it.containsCustomShortcuts }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
index bfc9486552a5..36c5ae0717be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
@@ -23,17 +23,12 @@ sealed interface ShortcutCustomizationUiState {
val shortcutLabel: String,
val errorMessage: String = "",
val defaultCustomShortcutModifierKey: ShortcutKey.Icon.ResIdIcon,
- val isDialogShowing: Boolean = false,
val pressedKeys: List<ShortcutKey> = emptyList(),
) : ShortcutCustomizationUiState
- data class DeleteShortcutDialog(
- val isDialogShowing: Boolean = false
- ) : ShortcutCustomizationUiState
+ data object DeleteShortcutDialog : ShortcutCustomizationUiState
- data class ResetShortcutDialog(
- val isDialogShowing: Boolean = false
- ) : ShortcutCustomizationUiState
+ data object ResetShortcutDialog : ShortcutCustomizationUiState
data object Inactive : ShortcutCustomizationUiState
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
index 02b0b43ea979..52ab157a0e70 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
@@ -25,6 +25,7 @@ sealed interface ShortcutsUiState {
val shortcutCategories: List<ShortcutCategoryUi>,
val defaultSelectedCategory: ShortcutCategoryType?,
val isShortcutCustomizerFlagEnabled: Boolean = false,
+ val shouldShowResetButton: Boolean = false,
) : ShortcutsUiState
data object Inactive : ShortcutsUiState
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 76a2e6095f01..92e25929fe4f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -73,32 +73,22 @@ constructor(
shortcutLabel = requestInfo.label,
defaultCustomShortcutModifierKey =
shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(),
- isDialogShowing = false,
pressedKeys = emptyList(),
)
shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
}
is ShortcutCustomizationRequestInfo.Delete -> {
- _shortcutCustomizationUiState.value = DeleteShortcutDialog(isDialogShowing = false)
+ _shortcutCustomizationUiState.value = DeleteShortcutDialog
shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
}
ShortcutCustomizationRequestInfo.Reset -> {
- _shortcutCustomizationUiState.value = ResetShortcutDialog(isDialogShowing = false)
+ _shortcutCustomizationUiState.value = ResetShortcutDialog
}
}
}
- fun onDialogShown() {
- _shortcutCustomizationUiState.update { uiState ->
- (uiState as? AddShortcutDialog)?.copy(isDialogShowing = true)
- ?: (uiState as? DeleteShortcutDialog)?.copy(isDialogShowing = true)
- ?: (uiState as? ResetShortcutDialog)?.copy(isDialogShowing = true)
- ?: uiState
- }
- }
-
fun onDialogDismissed() {
_shortcutCustomizationUiState.value = ShortcutCustomizationUiState.Inactive
shortcutCustomizationInteractor.onCustomizationRequested(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 08fd0af81006..0f5f07393114 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.shortcut.ui.viewmodel
import android.app.role.RoleManager
+import android.content.Context
import android.content.pm.PackageManager.NameNotFoundException
import android.util.Log
import androidx.compose.material.icons.Icons
@@ -40,7 +41,6 @@ import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -51,10 +51,12 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
+import javax.inject.Inject
class ShortcutHelperViewModel
@Inject
constructor(
+ private val context: Context,
private val roleManager: RoleManager,
private val userTracker: UserTracker,
@Background private val backgroundScope: CoroutineScope,
@@ -88,6 +90,7 @@ constructor(
shortcutCategories = shortcutCategoriesUi,
defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories),
isShortcutCustomizerFlagEnabled = keyboardShortcutHelperShortcutCustomizer(),
+ shouldShowResetButton = shouldShowResetButton(shortcutCategoriesUi)
)
}
}
@@ -97,6 +100,10 @@ constructor(
initialValue = ShortcutsUiState.Inactive,
)
+ private fun shouldShowResetButton(categoriesUi: List<ShortcutCategoryUi>): Boolean {
+ return categoriesUi.any { it.containsCustomShortcuts }
+ }
+
private fun convertCategoriesModelToUiModel(
categories: List<ShortcutCategory>
): List<ShortcutCategoryUi> {
@@ -136,13 +143,13 @@ constructor(
private fun getShortcutCategoryLabel(type: ShortcutCategoryType): String =
when (type) {
ShortcutCategoryType.System ->
- userContext.getString(R.string.shortcut_helper_category_system)
+ context.getString(R.string.shortcut_helper_category_system)
ShortcutCategoryType.MultiTasking ->
- userContext.getString(R.string.shortcut_helper_category_multitasking)
+ context.getString(R.string.shortcut_helper_category_multitasking)
ShortcutCategoryType.InputMethodEditor ->
- userContext.getString(R.string.shortcut_helper_category_input)
+ context.getString(R.string.shortcut_helper_category_input)
ShortcutCategoryType.AppCategories ->
- userContext.getString(R.string.shortcut_helper_category_app_shortcuts)
+ context.getString(R.string.shortcut_helper_category_app_shortcuts)
is CurrentApp -> getApplicationLabelForCurrentApp(type)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
index 1def7b393435..636f703ac65a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
@@ -19,7 +19,6 @@ package com.android.systemui.qs.panels.data.repository
import android.content.res.Resources
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.emitOnStart
@@ -30,7 +29,7 @@ import kotlinx.coroutines.flow.map
class QuickQuickSettingsRowRepository
@Inject
constructor(
- @Main private val resources: Resources,
+ @ShadeDisplayAware private val resources: Resources,
@ShadeDisplayAware configurationRepository: ConfigurationRepository,
) {
val rows =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 75debb6c4032..9efdd98df4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -9,6 +9,7 @@ import android.provider.AlarmClock
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.text.format.DateFormat
+import android.widget.Button
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
@@ -70,7 +71,10 @@ constructor(
}
override fun newTileState(): QSTile.State {
- return QSTile.State().apply { handlesLongClick = false }
+ return QSTile.State().apply {
+ handlesLongClick = false
+ expandedAccessibilityClassName = Button::class.java.name
+ }
}
override fun handleClick(expandable: Expandable?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 404ace18d0f9..7213f7a60da0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -21,6 +21,7 @@ import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.service.quicksettings.Tile
+import android.widget.Button
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.animation.Expandable
@@ -163,6 +164,7 @@ constructor(
} else {
state.state = Tile.STATE_UNAVAILABLE
}
+ state.expandedAccessibilityClassName = Button::class.java.name
}
override fun getMetricsCategory(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index 374bcda5f46a..e37ed16133e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -32,6 +32,7 @@ import android.service.dreams.IDreamManager;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.Switch;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -176,6 +177,7 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> {
} else {
state.state = isDreaming() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
+ state.expandedAccessibilityClassName = Switch.class.getName();
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 48b39dcf29ba..74563fff8775 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -24,6 +24,7 @@ import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.service.quicksettings.Tile;
+import android.widget.Button;
import androidx.annotation.Nullable;
@@ -124,6 +125,7 @@ public class HearingDevicesTile extends QSTileImpl<BooleanState> {
state.state = Tile.STATE_INACTIVE;
state.secondaryLabel = "";
}
+ state.expandedAccessibilityClassName = Button.class.getName();
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index e9c5f4ae9b10..0a5952997893 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -30,7 +30,7 @@ import android.service.quicksettings.Tile;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.Switch;
+import android.widget.Button;
import androidx.annotation.Nullable;
@@ -136,7 +136,7 @@ public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
}
@Override
- public void secondaryClick(@Nullable Expandable expandable) {
+ public void handleSecondaryClick(@Nullable Expandable expandable) {
// TODO(b/358352265): Figure out the correct action for the secondary click
// Toggle Wifi
mWifiStateWorker.setWifiEnabled(!mWifiStateWorker.isWifiEnabled());
@@ -577,7 +577,7 @@ public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
- state.expandedAccessibilityClassName = Switch.class.getName();
+ state.expandedAccessibilityClassName = Button.class.getName();
if (DEBUG) {
Log.d(TAG, "handleUpdateWifiState: " + "BooleanState = " + state.toString());
}
@@ -594,7 +594,7 @@ public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
boolean mobileDataEnabled = mDataController.isMobileDataSupported()
&& mDataController.isMobileDataEnabled();
state.value = mobileDataEnabled;
- state.expandedAccessibilityClassName = Switch.class.getName();
+ state.expandedAccessibilityClassName = Button.class.getName();
if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) {
state.state = Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 7225800e25e3..6d3e5d07c251 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -20,7 +20,7 @@ import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.provider.Settings
-import android.widget.Switch
+import android.widget.Button
import com.android.internal.logging.MetricsLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Background
@@ -71,7 +71,7 @@ constructor(
metricsLogger,
statusBarStateController,
activityStarter,
- qsLogger
+ qsLogger,
) {
private var model: InternetTileModel = viewModel.tileModel.value
@@ -110,7 +110,7 @@ constructor(
return InternetDetailsViewModel { longClick(null) }
}
- override fun secondaryClick(expandable: Expandable?) {
+ override fun handleSecondaryClick(expandable: Expandable?) {
// TODO(b/358352265): Figure out the correct action for the secondary click
// Toggle wifi
wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
@@ -118,7 +118,7 @@ constructor(
override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
- state.expandedAccessibilityClassName = Switch::class.java.name
+ state.expandedAccessibilityClassName = Button::class.java.name
model.applyTo(state, mContext)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 93a51cfacf02..467233d5f71f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -21,6 +21,7 @@ import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.Tile;
import android.util.Log;
+import android.widget.Button;
import androidx.annotation.Nullable;
@@ -126,6 +127,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
// would go to "Unavailable" state only when GMS core is updating.
state.secondaryLabel = state.state == Tile.STATE_UNAVAILABLE
? mContext.getString(R.string.qr_code_scanner_updating_secondary_label) : null;
+ state.expandedAccessibilityClassName = Button.class.getName();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 3d039e6ef824..6deb19257591 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -40,6 +40,7 @@ import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.WalletCard;
import android.service.quicksettings.Tile;
import android.util.Log;
+import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -142,8 +143,16 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
mUiHandler.post(
- () -> mController.startQuickAccessUiIntent(
- mActivityStarter, animationController, mSelectedCard != null));
+ () -> {
+ if (android.service.quickaccesswallet.Flags.launchSelectedCardFromQsTile()
+ && mSelectedCard != null) {
+ mController.startWalletCardPendingIntent(
+ mSelectedCard, mActivityStarter, animationController);
+ } else {
+ mController.startQuickAccessUiIntent(
+ mActivityStarter, animationController, mSelectedCard != null);
+ }
+ });
}
@Override
@@ -178,6 +187,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
state.secondaryLabel = null;
state.sideViewCustomDrawable = null;
}
+ state.expandedAccessibilityClassName = Button.class.getName();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 87f542e6ab39..aeb6cef162b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.base.viewmodel
import android.os.UserHandle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Dumpable
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
@@ -58,11 +59,8 @@ import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
-import kotlinx.coroutines.withContext
/**
* Provides a hassle-free way to implement new tiles according to current System UI architecture
@@ -90,36 +88,35 @@ class QSTileViewModelImpl<DATA_TYPE>(
private val users: MutableStateFlow<UserHandle> =
MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
+
private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow()
+
private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow()
private val spec
get() = config.tileSpec
- private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow()
+ private val tileData: SharedFlow<DATA_TYPE?> = createTileDataFlow()
override val state: StateFlow<QSTileState?> =
tileData
.map { data ->
- withContext(uiBackgroundDispatcher) { mapper().map(config, data) }
- .also { state -> qsTileLogger.logStateUpdate(spec, state, data) }
+ data?.let {
+ mapper().map(config, it).also { state ->
+ qsTileLogger.logStateUpdate(spec, state, it)
+ }
+ }
}
- .stateIn(
- tileScope,
- SharingStarted.WhileSubscribed(),
- null,
- )
+ .flowOn(uiBackgroundDispatcher)
+ .stateIn(tileScope, SharingStarted.WhileSubscribed(), null)
+
override val isAvailable: StateFlow<Boolean> =
users
.flatMapLatest { tileDataInteractor().availability(it) }
.flowOn(backgroundDispatcher)
- .stateIn(
- tileScope,
- SharingStarted.WhileSubscribed(),
- true,
- )
+ .stateIn(tileScope, SharingStarted.WhileSubscribed(), true)
override fun forceUpdate() {
- tileScope.launch { forceUpdates.emit(Unit) }
+ tileScope.launch(context = backgroundDispatcher) { forceUpdates.emit(Unit) }
}
override fun onUserChanged(user: UserHandle) {
@@ -131,9 +128,9 @@ class QSTileViewModelImpl<DATA_TYPE>(
userAction,
spec,
tileData.replayCache.isNotEmpty(),
- state.replayCache.isNotEmpty()
+ state.replayCache.isNotEmpty(),
)
- tileScope.launch { userInputs.emit(userAction) }
+ tileScope.launch(context = backgroundDispatcher) { userInputs.emit(userAction) }
}
override fun destroy() {
@@ -147,7 +144,7 @@ class QSTileViewModelImpl<DATA_TYPE>(
println(state.replayCache.lastOrNull().toString())
}
- private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
+ private fun createTileDataFlow(): SharedFlow<DATA_TYPE?> =
users
.transformLatest { user ->
coroutineScope {
@@ -159,6 +156,7 @@ class QSTileViewModelImpl<DATA_TYPE>(
.onEach { qsTileLogger.logForceUpdate(spec) },
)
.onStart { qsTileLogger.logInitialRequest(spec) }
+ .flowOn(backgroundDispatcher)
.stateIn(this, SharingStarted.Eagerly, DataUpdateTrigger.InitialRequest)
tileDataInteractor()
.tileData(user, updateTriggers)
@@ -171,11 +169,8 @@ class QSTileViewModelImpl<DATA_TYPE>(
}
}
.distinctUntilChanged()
- .shareIn(
- tileScope,
- SharingStarted.WhileSubscribed(),
- replay = 1, // we only care about the most recent value
- )
+ .flowOn(backgroundDispatcher)
+ .stateIn(tileScope, SharingStarted.WhileSubscribed(), null)
/**
* Creates a user input flow which:
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
index 8f870d468997..4806c3f83224 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.custom.domain.interactor
import android.os.UserHandle
import android.service.quicksettings.Tile
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
@@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePacka
import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,7 +45,6 @@ import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
-import com.android.app.tracing.coroutines.launchTraced as launch
@QSTileScope
@OptIn(ExperimentalCoroutinesApi::class)
@@ -64,7 +64,7 @@ constructor(
private val bindingFlow =
mutableUserFlow
.flatMapLatest { user ->
- ConflatedCallbackFlow.conflatedCallbackFlow {
+ conflatedCallbackFlow {
serviceInteractor.setUser(user)
// Wait for the CustomTileInteractor to become initialized first, because
@@ -79,7 +79,7 @@ constructor(
defaultsRepository.requestNewDefaults(
user,
tileSpec.componentName,
- true
+ true,
)
}
.launchIn(this)
@@ -99,7 +99,7 @@ constructor(
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<CustomTileDataModel> {
tileScope.launch { mutableUserFlow.emit(user) }
return bindingFlow.combine(triggers) { _, _ -> }.flatMapLatest { dataFlow(user) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index ab3862b75ee8..f9a1ad5d8424 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -25,6 +25,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
@@ -35,14 +36,17 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.io.PrintWriter
import java.util.concurrent.CopyOnWriteArraySet
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.launch
// TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
class QSTileViewModelAdapter
@@ -51,6 +55,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
private val qsHost: QSHost,
@Assisted private val qsTileViewModel: QSTileViewModel,
+ @UiBackground private val uiBgDispatcher: CoroutineDispatcher,
) : QSTile, Dumpable {
private val context
@@ -162,19 +167,25 @@ constructor(
override fun setListening(client: Any?, listening: Boolean) {
client ?: return
if (listening) {
- val clientWasNotAlreadyListening = listeningClients.add(client)
- if (clientWasNotAlreadyListening && listeningClients.size == 1) {
- stateJob =
- qsTileViewModel.state
- .filterNotNull()
- .map { mapState(context, it, qsTileViewModel.config) }
- .onEach { legacyState ->
- val changed = legacyState.copyTo(cachedState)
- if (changed) {
- callbacks.forEach { it.onStateChanged(legacyState) }
+ applicationScope.launch(uiBgDispatcher) {
+ val shouldStartMappingJob =
+ listeningClients.add(client) // new client
+ && listeningClients.size == 1 // first client
+
+ if (shouldStartMappingJob) {
+ stateJob =
+ qsTileViewModel.state
+ .filterNotNull()
+ .map { mapState(context, it, qsTileViewModel.config) }
+ .onEach { legacyState ->
+ val changed = legacyState.copyTo(cachedState)
+ if (changed) {
+ callbacks.forEach { it.onStateChanged(legacyState) }
+ }
}
- }
- .launchIn(applicationScope)
+ .flowOn(uiBgDispatcher)
+ .launchIn(applicationScope)
+ }
}
} else {
listeningClients.remove(client)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 88522d559c30..3a6c250e55f1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -172,6 +172,7 @@ import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirr
import com.android.systemui.shade.data.repository.FlingInfo;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
@@ -2269,7 +2270,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
float getDisplayDensity() {
- return mCentralSurfaces.getDisplayDensity();
+ if (ShadeWindowGoesAround.isEnabled()) {
+ return mView.getContext().getResources().getConfiguration().densityDpi;
+ } else {
+ return mCentralSurfaces.getDisplayDensity();
+ }
}
/** Return whether a touch is near the gesture handle at the bottom of screen */
@@ -3830,7 +3835,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/* screenOnFromTouch=*/ getWakefulness().isAwakeFromTapOrGesture());
// Log collapse gesture if on lock screen.
if (!expand && onKeyguard) {
- float displayDensity = mCentralSurfaces.getDisplayDensity();
+ float displayDensity = getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialExpandY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index bb0467f10e16..2eae3eb4fc19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -23,6 +23,8 @@ import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -37,17 +39,30 @@ class CallChipInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
+ ongoingCallInteractor: OngoingCallInteractor,
repository: OngoingCallRepository,
@StatusBarChipsLog private val logger: LogBuffer,
) {
val ongoingCallState: StateFlow<OngoingCallModel> =
- repository.ongoingCallState
+ (if (StatusBarChipsModernization.isEnabled)
+ ongoingCallInteractor.ongoingCallState
+ else
+ repository.ongoingCallState)
.onEach {
- logger.log(TAG, LogLevel.INFO, { str1 = it::class.simpleName }, { "State: $str1" })
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = it::class.simpleName },
+ { "State: $str1" }
+ )
}
- .stateIn(scope, SharingStarted.Lazily, OngoingCallModel.NoCall)
+ .stateIn(
+ scope,
+ SharingStarted.Lazily,
+ OngoingCallModel.NoCall
+ )
companion object {
private val TAG = "OngoingCall".pad()
}
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index b8cdd2587774..6f491e7beec8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -59,7 +59,8 @@ constructor(
interactor.ongoingCallState
.map { state ->
when (state) {
- is OngoingCallModel.NoCall -> OngoingActivityChipModel.Hidden()
+ is OngoingCallModel.NoCall,
+ is OngoingCallModel.InCallWithVisibleApp -> OngoingActivityChipModel.Hidden()
is OngoingCallModel.InCall -> {
val icon =
if (
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 47b695e50a0e..2588c7ae2363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
@@ -63,11 +64,6 @@ interface StatusBarModule {
@Binds
@IntoMap
- @ClassKey(OngoingCallController::class)
- fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
-
- @Binds
- @IntoMap
@ClassKey(LightBarController::class)
fun lightBarControllerAsCoreStartable(controller: LightBarController): CoreStartable
@@ -90,6 +86,18 @@ interface StatusBarModule {
): LightBarController.Factory
companion object {
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(OngoingCallController::class)
+ fun ongoingCallController(
+ controller: OngoingCallController
+ ): CoreStartable =
+ if (StatusBarChipsModernization.isEnabled) {
+ CoreStartable.NOP
+ } else {
+ controller
+ }
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 6de4928cd0c1..e4768e83f7d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarCompone
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent.Startable;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization;
import com.android.systemui.statusbar.phone.ui.DarkIconManager;
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder;
@@ -960,7 +961,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mOngoingCallController.addCallback(mOngoingCallListener);
}
// TODO(b/364653005): Do we also need to set the secondary activity chip?
- mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
+ if (!StatusBarChipsModernization.isEnabled()) {
+ mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 78926c78a368..216630409160 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -60,7 +60,10 @@ import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-/** A controller to handle the ongoing call chip in the collapsed status bar. */
+/** A controller to handle the ongoing call chip in the collapsed status bar.
+ * @deprecated Use [OngoingCallInteractor] instead, which follows recommended architecture patterns
+ */
+@Deprecated("Use OngoingCallInteractor instead")
@SysUISingleton
class OngoingCallController
@Inject
@@ -165,6 +168,9 @@ constructor(
}
override fun start() {
+ if (StatusBarChipsModernization.isEnabled)
+ return
+
dumpManager.registerDumpable(this)
if (Flags.statusBarUseReposForCallChip()) {
@@ -201,6 +207,8 @@ constructor(
* [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment].
*/
fun setChipView(chipView: View) {
+ StatusBarChipsModernization.assertInLegacyMode()
+
tearDownChipView()
this.chipView = chipView
val backgroundView: ChipBackgroundContainer? =
@@ -217,6 +225,8 @@ constructor(
* Returns true if there's an active ongoing call that should be displayed in a status bar chip.
*/
fun hasOngoingCall(): Boolean {
+ StatusBarChipsModernization.assertInLegacyMode()
+
return callNotificationInfo?.isOngoing == true &&
// When the user is in the phone app, don't show the chip.
!uidObserver.isCallAppVisible
@@ -224,6 +234,8 @@ constructor(
/** Creates the right [OngoingCallModel] based on the call state. */
private fun getOngoingCallModel(): OngoingCallModel {
+ StatusBarChipsModernization.assertInLegacyMode()
+
if (hasOngoingCall()) {
val currentInfo =
callNotificationInfo
@@ -255,6 +267,8 @@ constructor(
}
override fun addCallback(listener: OngoingCallListener) {
+ StatusBarChipsModernization.assertInLegacyMode()
+
synchronized(mListeners) {
if (!mListeners.contains(listener)) {
mListeners.add(listener)
@@ -263,10 +277,14 @@ constructor(
}
override fun removeCallback(listener: OngoingCallListener) {
+ StatusBarChipsModernization.assertInLegacyMode()
+
synchronized(mListeners) { mListeners.remove(listener) }
}
private fun updateInfoFromNotifModel(notifModel: ActiveNotificationModel?) {
+ StatusBarChipsModernization.assertInLegacyMode()
+
if (notifModel == null) {
logger.log(TAG, LogLevel.DEBUG, {}, { "NotifInteractorCallModel: null" })
removeChip()
@@ -310,6 +328,8 @@ constructor(
}
private fun updateChip() {
+ StatusBarChipsModernization.assertInLegacyMode()
+
val currentCallNotificationInfo = callNotificationInfo ?: return
val currentChipView = chipView
@@ -360,6 +380,8 @@ constructor(
}
private fun updateChipClickListener() {
+ StatusBarChipsModernization.assertInLegacyMode()
+
if (Flags.statusBarScreenSharingChips()) {
return
}
@@ -386,10 +408,14 @@ constructor(
/** Returns true if the given [procState] represents a process that's visible to the user. */
private fun isProcessVisibleToUser(procState: Int): Boolean {
+ StatusBarChipsModernization.assertInLegacyMode()
+
return procState <= ActivityManager.PROCESS_STATE_TOP
}
private fun updateGestureListening() {
+ StatusBarChipsModernization.assertInLegacyMode()
+
if (
callNotificationInfo == null ||
callNotificationInfo?.statusBarSwipedAway == true ||
@@ -404,6 +430,8 @@ constructor(
}
private fun removeChip() {
+ StatusBarChipsModernization.assertInLegacyMode()
+
callNotificationInfo = null
if (!Flags.statusBarScreenSharingChips()) {
tearDownChipView()
@@ -432,6 +460,8 @@ constructor(
* detected.
*/
private fun onSwipeAwayGestureDetected() {
+ StatusBarChipsModernization.assertInLegacyMode()
+
logger.log(TAG, LogLevel.DEBUG, {}, { "Swipe away gesture detected" })
callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
@@ -441,6 +471,8 @@ constructor(
}
private fun sendStateChangeEvent() {
+ StatusBarChipsModernization.assertInLegacyMode()
+
ongoingCallRepository.setOngoingCallState(getOngoingCallModel())
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
new file mode 100644
index 000000000000..2ab2b68bbb2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) 2024 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.phone.ongoingcall
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status_bar_use_interactor_for_call_chip flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarChipsModernization {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.statusBarChipsModernization() && Flags.statusBarRootModernization()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index f16371ae7e21..b932c71ab426 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -20,6 +20,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -33,7 +34,9 @@ import kotlinx.coroutines.flow.asStateFlow
* [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController] and
* [com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore]. Instead, those two
* classes both refer to this repository.
+ * @deprecated Use [OngoingCallInteractor] instead.
*/
+@Deprecated("Use OngoingCallInteractor instead")
@SysUISingleton
class OngoingCallRepository
@Inject
@@ -49,6 +52,8 @@ constructor(
* [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
*/
fun setOngoingCallState(state: OngoingCallModel) {
+ StatusBarChipsModernization.assertInLegacyMode()
+
logger.log(
TAG,
LogLevel.DEBUG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
new file mode 100644
index 000000000000..1f7bd1418543
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 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.phone.ongoingcall.domain.interactor
+
+import com.android.systemui.activity.data.repository.ActivityManagerRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Interactor for determining whether to show a chip in the status bar for ongoing phone calls.
+ *
+ * This class monitors call notifications and the visibility of call apps to determine the appropriate
+ * chip state. It emits:
+ * * - [OngoingCallModel.NoCall] when there is no call notification
+ * * - [OngoingCallModel.InCallWithVisibleApp] when there is a call notification but the call app is visible
+ * * - [OngoingCallModel.InCall] when there is a call notification and the call app is not visible
+ * */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class OngoingCallInteractor @Inject constructor(
+ @Application private val scope: CoroutineScope,
+ activityManagerRepository: ActivityManagerRepository,
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ @OngoingCallLog private val logBuffer: LogBuffer,
+) {
+ private val logger = Logger(logBuffer, TAG)
+
+ /**
+ * The current state of ongoing calls.
+ */
+ val ongoingCallState: StateFlow<OngoingCallModel> =
+ activeNotificationsInteractor.ongoingCallNotification
+ .flatMapLatest { notificationModel ->
+ when (notificationModel) {
+ null -> {
+ logger.d("No active call notification - hiding chip")
+ flowOf(OngoingCallModel.NoCall)
+ }
+
+ else -> combine(
+ flowOf(notificationModel),
+ activityManagerRepository.createIsAppVisibleFlow(
+ creationUid = notificationModel.uid,
+ logger = logger,
+ identifyingLogTag = TAG,
+ ),
+ ) { model, isVisible ->
+ when {
+ isVisible -> {
+ logger.d({ "Call app is visible: uid=$int1" }) {
+ int1 = model.uid
+ }
+ OngoingCallModel.InCallWithVisibleApp
+ }
+
+ else -> {
+ logger.d({ "Active call detected: startTime=$long1 hasIcon=$bool1" }) {
+ long1 = model.whenTime
+ bool1 = model.statusBarChipIconView != null
+ }
+ OngoingCallModel.InCall(
+ startTimeMs = model.whenTime,
+ notificationIconView = model.statusBarChipIconView,
+ intent = model.contentIntent,
+ )
+ }
+ }
+ }
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingCallModel.NoCall)
+
+ companion object {
+ private val TAG = "OngoingCall"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 34bff80ea919..c2c91b27d074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -25,6 +25,12 @@ sealed interface OngoingCallModel {
data object NoCall : OngoingCallModel
/**
+ * There is an ongoing call but the call app is currently visible, so we don't need to show
+ * the chip.
+ */
+ data object InCallWithVisibleApp : OngoingCallModel
+
+ /**
* There *is* an ongoing call.
*
* @property startTimeMs the time that the phone call started, based on the notification's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 1e8b0166409c..812e0eb0908f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarView
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ui.DarkIconManager
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
@@ -150,12 +151,11 @@ fun StatusBarRoot(
)
iconController.addIconGroup(darkIconManager)
- // TODO(b/372657935): This won't be needed once OngoingCallController is
- // implemented in recommended architecture
- ongoingCallController.setChipView(
- phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary)
- )
-
+ if (!StatusBarChipsModernization.isEnabled) {
+ ongoingCallController.setChipView(
+ phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary)
+ )
+ }
// For notifications, first inflate the [NotificationIconContainer]
val notificationIconArea =
phoneStatusBarView.requireViewById<ViewGroup>(R.id.notification_icon_area)
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 1d32a4fd69d6..389b6fb4b0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -32,6 +32,7 @@ import android.provider.Settings;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
+import android.service.quickaccesswallet.WalletCard;
import android.util.Log;
import com.android.systemui.animation.ActivityTransitionAnimator;
@@ -268,6 +269,23 @@ public class QuickAccessWalletController {
});
}
+ /**
+ * Starts the {@link android.app.PendingIntent} for a {@link WalletCard}.
+ *
+ * This should be used to open a selected card from the QuickAccessWallet UI or
+ * the settings tile.
+ *
+ * @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent.
+ * @param animationController an {@link ActivityTransitionAnimator.Controller} to provide a
+ * smooth animation for the activity launch.
+ */
+ public void startWalletCardPendingIntent(WalletCard card,
+ ActivityStarter activityStarter,
+ ActivityTransitionAnimator.Controller animationController) {
+ activityStarter.postStartActivityDismissingKeyguard(
+ card.getPendingIntent(), animationController);
+ }
+
private Intent getSysUiWalletIntent() {
return new Intent(mContext, WalletActivity.class)
.setAction(Intent.ACTION_VIEW);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
index 4ca84c58f6d9..50fad3bf697e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
@@ -32,6 +32,7 @@ import com.android.systemui.communal.data.backup.CommunalBackupUtilsTest.FakeWid
import com.android.systemui.communal.data.db.CommunalDatabase
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.proto.toCommunalHubState
+import com.android.systemui.communal.shared.model.SpanValue
import com.android.systemui.lifecycle.InstantTaskExecutorRule
import com.google.common.truth.Truth.assertThat
import java.io.File
@@ -120,21 +121,32 @@ class CommunalBackupHelperTest : SysuiTestCase() {
componentName = "com.android.fakePackage1/fakeWidget1",
rank = 0,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(1),
),
FakeWidgetMetadata(
widgetId = 12,
componentName = "com.android.fakePackage2/fakeWidget2",
rank = 1,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(2),
),
FakeWidgetMetadata(
widgetId = 13,
componentName = "com.android.fakePackage3/fakeWidget3",
rank = 2,
userSerialNumber = 10,
+ spanY = SpanValue.Responsive(3),
),
)
- .onEach { dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber) }
+ .onEach {
+ dao.addWidget(
+ widgetId = it.widgetId,
+ componentName = it.componentName,
+ rank = it.rank,
+ userSerialNumber = it.userSerialNumber,
+ spanY = it.spanY,
+ )
+ }
}
private fun getBackupDataInputStream(): BackupDataInputStream {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
index edc8c837bf78..d31e4664d4a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
@@ -23,6 +23,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.db.CommunalDatabase
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.nano.CommunalHubState
+import com.android.systemui.communal.shared.model.SpanValue
+import com.android.systemui.communal.shared.model.toFixed
+import com.android.systemui.communal.shared.model.toResponsive
import com.android.systemui.lifecycle.InstantTaskExecutorRule
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
@@ -71,22 +74,25 @@ class CommunalBackupUtilsTest : SysuiTestCase() {
componentName = "com.android.fakePackage1/fakeWidget1",
rank = 0,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(1),
),
FakeWidgetMetadata(
widgetId = 12,
componentName = "com.android.fakePackage2/fakeWidget2",
rank = 1,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(2),
),
FakeWidgetMetadata(
widgetId = 13,
componentName = "com.android.fakePackage3/fakeWidget3",
rank = 2,
userSerialNumber = 10,
+ spanY = SpanValue.Responsive(3),
),
)
expectedWidgets.forEach {
- dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber)
+ dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber, it.spanY)
}
// Get communal hub state
@@ -150,6 +156,7 @@ class CommunalBackupUtilsTest : SysuiTestCase() {
val componentName: String,
val rank: Int,
val userSerialNumber: Int,
+ val spanY: SpanValue,
)
companion object {
@@ -163,7 +170,9 @@ class CommunalBackupUtilsTest : SysuiTestCase() {
actual?.widgetId == expected?.widgetId &&
actual?.componentName == expected?.componentName &&
actual?.rank == expected?.rank &&
- actual?.userSerialNumber == expected?.userSerialNumber
+ actual?.userSerialNumber == expected?.userSerialNumber &&
+ actual?.spanY == expected?.spanY?.toFixed()?.value &&
+ actual?.spanYNew == expected?.spanY?.toResponsive()?.value
},
"represents",
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
index 7d5a334b45ea..1466e32b1296 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
@@ -22,6 +22,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.SpanValue
+import com.android.systemui.communal.shared.model.toResponsive
import com.android.systemui.lifecycle.InstantTaskExecutorRule
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -173,6 +175,49 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() {
databaseV4.verifyWidgetsV4(fakeWidgetsV3.map { it.getV4() })
}
+ @Test
+ fun migrate4To5_addNewSpanYColumn() {
+ val databaseV4 = migrationTestHelper.createDatabase(DATABASE_NAME, version = 4)
+
+ val fakeWidgetsV4 =
+ listOf(
+ FakeCommunalWidgetItemV4(
+ widgetId = 1,
+ componentName = "test_widget_1",
+ itemId = 11,
+ userSerialNumber = 0,
+ spanY = 3,
+ ),
+ FakeCommunalWidgetItemV4(
+ widgetId = 2,
+ componentName = "test_widget_2",
+ itemId = 12,
+ userSerialNumber = 10,
+ spanY = 6,
+ ),
+ FakeCommunalWidgetItemV4(
+ widgetId = 3,
+ componentName = "test_widget_3",
+ itemId = 13,
+ userSerialNumber = 0,
+ spanY = 0,
+ ),
+ )
+ databaseV4.insertWidgetsV4(fakeWidgetsV4)
+
+ databaseV4.verifyWidgetsV4(fakeWidgetsV4)
+
+ val databaseV5 =
+ migrationTestHelper.runMigrationsAndValidate(
+ name = DATABASE_NAME,
+ version = 5,
+ validateDroppedTables = false,
+ CommunalDatabase.MIGRATION_4_5,
+ )
+
+ databaseV5.verifyWidgetsV5(fakeWidgetsV4.map { it.getV5() })
+ }
+
private fun SupportSQLiteDatabase.insertWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) {
widgets.forEach { widget ->
execSQL(
@@ -198,6 +243,24 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() {
}
}
+ private fun SupportSQLiteDatabase.insertWidgetsV4(widgets: List<FakeCommunalWidgetItemV4>) {
+ widgets.forEach { widget ->
+ execSQL(
+ "INSERT INTO communal_widget_table(" +
+ "widget_id, " +
+ "component_name, " +
+ "item_id, " +
+ "user_serial_number, " +
+ "span_y) " +
+ "VALUES(${widget.widgetId}, " +
+ "'${widget.componentName}', " +
+ "${widget.itemId}, " +
+ "${widget.userSerialNumber}," +
+ "${widget.spanY})"
+ )
+ }
+ }
+
private fun SupportSQLiteDatabase.verifyWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) {
val cursor = query("SELECT * FROM communal_widget_table")
assertThat(cursor.moveToFirst()).isTrue()
@@ -270,6 +333,27 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() {
assertThat(cursor.isAfterLast).isTrue()
}
+ private fun SupportSQLiteDatabase.verifyWidgetsV5(widgets: List<FakeCommunalWidgetItemV5>) {
+ val cursor = query("SELECT * FROM communal_widget_table")
+ assertThat(cursor.moveToFirst()).isTrue()
+
+ widgets.forEach { widget ->
+ assertThat(cursor.getInt(cursor.getColumnIndex("widget_id"))).isEqualTo(widget.widgetId)
+ assertThat(cursor.getString(cursor.getColumnIndex("component_name")))
+ .isEqualTo(widget.componentName)
+ assertThat(cursor.getInt(cursor.getColumnIndex("item_id"))).isEqualTo(widget.itemId)
+ assertThat(cursor.getInt(cursor.getColumnIndex("user_serial_number")))
+ .isEqualTo(widget.userSerialNumber)
+ assertThat(cursor.getInt(cursor.getColumnIndex("span_y"))).isEqualTo(widget.spanY)
+ assertThat(cursor.getInt(cursor.getColumnIndex("span_y_new")))
+ .isEqualTo(widget.spanYNew)
+
+ cursor.moveToNext()
+ }
+
+ assertThat(cursor.isAfterLast).isTrue()
+ }
+
private fun SupportSQLiteDatabase.insertRanks(ranks: List<FakeCommunalItemRank>) {
ranks.forEach { rank ->
execSQL("INSERT INTO communal_item_rank_table(rank) VALUES(${rank.rank})")
@@ -334,6 +418,27 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() {
val spanY: Int,
)
+ private fun FakeCommunalWidgetItemV4.getV5(): FakeCommunalWidgetItemV5 {
+ val spanYFixed = SpanValue.Fixed(spanY)
+ return FakeCommunalWidgetItemV5(
+ widgetId = widgetId,
+ componentName = componentName,
+ itemId = itemId,
+ userSerialNumber = userSerialNumber,
+ spanY = spanYFixed.value,
+ spanYNew = spanYFixed.toResponsive().value,
+ )
+ }
+
+ private data class FakeCommunalWidgetItemV5(
+ val widgetId: Int,
+ val componentName: String,
+ val itemId: Int,
+ val userSerialNumber: Int,
+ val spanY: Int,
+ val spanYNew: Int,
+ )
+
private data class FakeCommunalItemRank(val rank: Int)
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
index 2312bbd2d7f8..2acb7750273b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -22,7 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.nano.CommunalHubState
-import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.SpanValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.lifecycle.InstantTaskExecutorRule
import com.google.common.truth.Truth.assertThat
@@ -68,12 +68,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
@Test
fun addWidget_readValueInDb() =
testScope.runTest {
- val (widgetId, provider, rank, userSerialNumber) = widgetInfo1
+ val (widgetId, provider, rank, userSerialNumber, spanY) = widgetInfo1
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
rank = rank,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
val entry = communalWidgetDao.getWidgetByIdNow(id = 1)
assertThat(entry).isEqualTo(communalWidgetItemEntry1)
@@ -82,12 +83,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
@Test
fun deleteWidget_notInDb_returnsFalse() =
testScope.runTest {
- val (widgetId, provider, rank, userSerialNumber) = widgetInfo1
+ val (widgetId, provider, rank, userSerialNumber, spanY) = widgetInfo1
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
rank = rank,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
assertThat(communalWidgetDao.deleteWidgetById(widgetId = 123)).isFalse()
}
@@ -98,12 +100,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
val widgets = collectLastValue(communalWidgetDao.getWidgets())
widgetsToAdd.forEach {
- val (widgetId, provider, rank, userSerialNumber) = it
+ val (widgetId, provider, rank, userSerialNumber, spanY) = it
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
rank = rank,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
}
assertThat(widgets())
@@ -126,11 +129,12 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
// Add widgets one by one without specifying rank
val widgetsToAdd = listOf(widgetInfo1, widgetInfo2, widgetInfo3)
widgetsToAdd.forEach {
- val (widgetId, provider, _, userSerialNumber) = it
+ val (widgetId, provider, _, userSerialNumber, spanY) = it
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
}
@@ -153,12 +157,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
val widgets = collectLastValue(communalWidgetDao.getWidgets())
widgetsToAdd.forEach {
- val (widgetId, provider, rank, userSerialNumber) = it
+ val (widgetId, provider, rank, userSerialNumber, spanY) = it
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
rank = rank,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
}
assertThat(widgets())
@@ -180,12 +185,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
val widgets = collectLastValue(communalWidgetDao.getWidgets())
widgetsToAdd.forEach {
- val (widgetId, provider, rank, userSerialNumber) = it
+ val (widgetId, provider, rank, userSerialNumber, spanY) = it
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
rank = rank,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
}
assertThat(widgets())
@@ -217,12 +223,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
val widgets = collectLastValue(communalWidgetDao.getWidgets())
existingWidgets.forEach {
- val (widgetId, provider, rank, userSerialNumber) = it
+ val (widgetId, provider, rank, userSerialNumber, spanY) = it
communalWidgetDao.addWidget(
widgetId = widgetId,
provider = provider,
rank = rank,
userSerialNumber = userSerialNumber,
+ spanY = spanY,
)
}
assertThat(widgets())
@@ -242,6 +249,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
provider = ComponentName("pk_name", "cls_name_4"),
rank = 1,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(1),
)
val newRankEntry = CommunalItemRank(uid = 4L, rank = 1)
@@ -253,6 +261,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
itemId = 4L,
userSerialNumber = 0,
spanY = 3,
+ spanYNew = 1,
)
assertThat(widgets())
.containsExactly(
@@ -279,21 +288,21 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
provider = ComponentName("pkg_name", "cls_name_1"),
rank = 0,
userSerialNumber = 0,
- spanY = CommunalContentSize.FULL.span,
+ spanY = SpanValue.Responsive(1),
)
communalWidgetDao.addWidget(
widgetId = 2,
provider = ComponentName("pkg_name", "cls_name_2"),
rank = 1,
userSerialNumber = 0,
- spanY = CommunalContentSize.HALF.span,
+ spanY = SpanValue.Responsive(2),
)
communalWidgetDao.addWidget(
widgetId = 3,
provider = ComponentName("pkg_name", "cls_name_3"),
rank = 2,
userSerialNumber = 0,
- spanY = CommunalContentSize.THIRD.span,
+ spanY = SpanValue.Fixed(3),
)
// Verify that the widgets have the correct spanY values
@@ -306,7 +315,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
componentName = "pkg_name/cls_name_1",
itemId = 1L,
userSerialNumber = 0,
- spanY = CommunalContentSize.FULL.span,
+ spanY = 3,
+ spanYNew = 1,
),
CommunalItemRank(uid = 2L, rank = 1),
CommunalWidgetItem(
@@ -315,7 +325,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
componentName = "pkg_name/cls_name_2",
itemId = 2L,
userSerialNumber = 0,
- spanY = CommunalContentSize.HALF.span,
+ spanY = 6,
+ spanYNew = 2,
),
CommunalItemRank(uid = 3L, rank = 2),
CommunalWidgetItem(
@@ -324,7 +335,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
componentName = "pkg_name/cls_name_3",
itemId = 3L,
userSerialNumber = 0,
- spanY = CommunalContentSize.THIRD.span,
+ spanY = 3,
+ spanYNew = 1,
),
)
.inOrder()
@@ -352,7 +364,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
componentName = fakeWidget.componentName,
itemId = rank.uid,
userSerialNumber = fakeWidget.userSerialNumber,
- spanY = 3,
+ spanY = fakeWidget.spanY.coerceAtLeast(3),
+ spanYNew = fakeWidget.spanYNew.coerceAtLeast(1),
)
expected[rank] = widget
}
@@ -366,6 +379,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
provider = metadata.provider,
rank = rank ?: metadata.rank,
userSerialNumber = metadata.userSerialNumber,
+ spanY = metadata.spanY,
)
}
@@ -374,6 +388,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
val provider: ComponentName,
val rank: Int,
val userSerialNumber: Int,
+ val spanY: SpanValue,
)
companion object {
@@ -383,6 +398,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
provider = ComponentName("pk_name", "cls_name_1"),
rank = 0,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(1),
)
val widgetInfo2 =
FakeWidgetMetadata(
@@ -390,6 +406,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
provider = ComponentName("pk_name", "cls_name_2"),
rank = 1,
userSerialNumber = 0,
+ spanY = SpanValue.Responsive(1),
)
val widgetInfo3 =
FakeWidgetMetadata(
@@ -397,6 +414,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
provider = ComponentName("pk_name", "cls_name_3"),
rank = 2,
userSerialNumber = 10,
+ spanY = SpanValue.Responsive(1),
)
val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.rank)
val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.rank)
@@ -409,6 +427,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
itemId = communalItemRankEntry1.uid,
userSerialNumber = widgetInfo1.userSerialNumber,
spanY = 3,
+ spanYNew = 1,
)
val communalWidgetItemEntry2 =
CommunalWidgetItem(
@@ -418,6 +437,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
itemId = communalItemRankEntry2.uid,
userSerialNumber = widgetInfo2.userSerialNumber,
spanY = 3,
+ spanYNew = 1,
)
val communalWidgetItemEntry3 =
CommunalWidgetItem(
@@ -427,6 +447,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
itemId = communalItemRankEntry3.uid,
userSerialNumber = widgetInfo3.userSerialNumber,
spanY = 3,
+ spanYNew = 1,
)
val fakeState =
CommunalHubState().apply {
@@ -437,12 +458,14 @@ class CommunalWidgetDaoTest : SysuiTestCase() {
componentName = "pk_name/fake_widget_1"
rank = 1
userSerialNumber = 0
+ spanY = 3
},
CommunalHubState.CommunalWidgetItem().apply {
widgetId = 2
componentName = "pk_name/fake_widget_2"
rank = 2
userSerialNumber = 10
+ spanYNew = 1
},
)
.toTypedArray()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d1e4f646a382..3e24fbe6cd8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization;
import com.android.systemui.statusbar.phone.ui.DarkIconManager;
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewBinder;
@@ -155,9 +156,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
any(StatusBarWindowStateListener.class));
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testDisableNone() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testDisableNone() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -166,9 +167,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -184,9 +185,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
// GIVEN the status bar hides the system info via disable flags, while there is no event
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -214,9 +215,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
// GIVEN the status bar hides the system info via disable flags, while there is no event
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -231,9 +232,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
// GIVEN the status bar is not disabled
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
@@ -247,9 +248,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
// GIVEN the status bar is not disabled
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
@@ -271,9 +272,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testDisableNotifications() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testDisableNotifications() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
@@ -307,9 +308,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testDisableClock() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testDisableClock() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
@@ -343,10 +344,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_shadeOpenAndShouldHide_everythingHidden() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_shadeOpenAndShouldHide_everythingHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the shade is open and configured to hide the status bar icons
@@ -361,10 +362,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_shadeOpenButNotShouldHide_everythingShown() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_shadeOpenButNotShouldHide_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the shade is open but *not* configured to hide the status bar icons
@@ -379,11 +380,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
- /** Regression test for b/279790651. */
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
+ /** Regression test for b/279790651. */
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the shade is open and configured to hide the status bar icons
@@ -409,9 +410,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_notTransitioningToOccluded_everythingShown() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_notTransitioningToOccluded_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(false);
@@ -424,10 +425,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_isTransitioningToOccluded_everythingHidden() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_isTransitioningToOccluded_everythingHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(true);
@@ -440,10 +441,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the transition is occurring
@@ -472,9 +473,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getUserChipView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void disable_noOngoingCall_chipHidden() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void disable_noOngoingCall_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -484,9 +489,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -497,9 +506,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -510,9 +523,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void disable_hasOngoingCallButAlsoHun_chipHidden() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void disable_hasOngoingCallButAlsoHun_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -523,9 +540,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void disable_ongoingCallEnded_chipHidden() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void disable_ongoingCallEnded_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Ongoing call started
@@ -547,9 +568,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -564,9 +589,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
- public void screenSharingChipsDisabled_ignoresNewCallback() {
+ @Test
+ @DisableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void screenSharingChipsDisabled_ignoresNewCallback() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -597,10 +626,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void noOngoingActivity_chipHidden() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void noOngoingActivity_chipHidden() {
resumeAndGetFragment();
// TODO(b/332662551): We *should* be able to just set a value on
@@ -615,10 +644,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -630,12 +659,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @EnableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarNotifChips.FLAG_NAME,
- StatusBarRootModernization.FLAG_NAME})
- public void hasPrimaryOngoingActivity_viewsUnchangedWhenRootModernizationFlagOn() {
+ @Test
+ @EnableFlags({
+ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void hasPrimaryOngoingActivity_viewsUnchangedWhenRootModernizationFlagOn() {
resumeAndGetFragment();
assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
@@ -658,10 +689,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
- public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -672,10 +707,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -687,10 +722,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
- public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -704,10 +743,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -722,10 +761,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
- public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -739,10 +782,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -757,10 +800,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
- public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
resumeAndGetFragment();
// Ongoing activity started
@@ -780,10 +827,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
resumeAndGetFragment();
// Ongoing activity started
@@ -803,10 +850,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void secondaryOngoingActivityEnded_chipHidden() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void secondaryOngoingActivityEnded_chipHidden() {
resumeAndGetFragment();
// Secondary ongoing activity started
@@ -826,10 +873,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
- public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -845,10 +896,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -864,10 +915,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
- public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ @DisableFlags({
+ StatusBarNotifChips.FLAG_NAME,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME
+ })
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -897,10 +952,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
+ @Test
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -931,10 +986,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getSecondaryOngoingActivityChipView().getVisibility());
}
- @Test
- @EnableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
+ @Test
+ @EnableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false);
@@ -945,10 +1000,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @EnableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void isHomeStatusBarAllowedByScene_true_everythingShown() {
+ @Test
+ @EnableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void isHomeStatusBarAllowedByScene_true_everythingShown() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(true);
@@ -959,10 +1014,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @EnableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
+ @Test
+ @EnableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the scene doesn't allow the status bar
@@ -977,10 +1032,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @EnableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
+ @Test
+ @EnableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the scene does allow the status bar
@@ -995,10 +1050,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
resumeAndGetFragment();
// Even if the scene says to hide the home status bar
@@ -1010,9 +1065,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_isDozing_clockAndSystemInfoVisible() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_isDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -1022,9 +1077,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_NotDozing_clockAndSystemInfoVisible() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_NotDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(false);
@@ -1034,9 +1089,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
@@ -1045,9 +1100,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.GONE, getClockView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
@@ -1098,10 +1153,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertFalse(contains);
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mockSecureCameraLaunch(fragment, true /* launched */);
@@ -1121,10 +1176,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
- @Test
- @DisableSceneContainer
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
+ @Test
+ @DisableSceneContainer
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN a transition to dream has started
@@ -1158,9 +1213,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
- @Test
- @DisableFlags(StatusBarRootModernization.FLAG_NAME)
- public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
+ @Test
+ @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+ public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN a transition to dream has started but we're *not* dreaming
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 153a8be06adc..3e44364dc6a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -118,6 +118,7 @@ public abstract class SysuiTestCase {
android.net.platform.flags.Flags.class,
android.os.Flags.class,
android.service.controls.flags.Flags.class,
+ android.service.quickaccesswallet.Flags.class,
com.android.internal.telephony.flags.Flags.class,
com.android.server.notification.Flags.class,
com.android.systemui.Flags.class);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 3b175853de7d..1f7f3bc4be2b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -55,7 +55,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
rank: Int = 0,
category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
userId: Int = 0,
- spanY: Int = CommunalContentSize.HALF.span,
+ spanY: Int = CommunalContentSize.FixedSize.HALF.span,
) {
fakeDatabase[appWidgetId] =
CommunalWidgetContentModel.Available(
@@ -87,7 +87,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
componentName = ComponentName.unflattenFromString(componentName)!!,
icon = icon,
user = UserHandle(userId),
- spanY = CommunalContentSize.HALF.span,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
)
updateListFromDatabase()
}
@@ -143,7 +143,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
appWidgetId = id,
providerInfo = providerInfo,
rank = rank,
- spanY = CommunalContentSize.HALF.span,
+ spanY = CommunalContentSize.FixedSize.HALF.span,
)
updateListFromDatabase()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 2bff0c66889f..1828da5c0790 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -160,6 +160,7 @@ val Kosmos.shortcutHelperCategoriesInteractor by
val Kosmos.shortcutHelperViewModel by
Kosmos.Fixture {
ShortcutHelperViewModel(
+ applicationContext,
mockRoleManager,
userTracker,
applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
index a90876551d20..de9f629ef787 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.util.mockito.mock
val Kosmos.qsTileViewModelAdaperFactory by
@@ -28,6 +29,7 @@ val Kosmos.qsTileViewModelAdaperFactory by
applicationCoroutineScope,
mock(),
qsTileViewModel,
+ testDispatcher,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
index fcd14d8512fb..d8d4d2b65eff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
@@ -20,12 +20,14 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.chips.statusBarChipsLogger
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.ongoingCallInteractor
val Kosmos.callChipInteractor: CallChipInteractor by
Kosmos.Fixture {
CallChipInteractor(
scope = applicationCoroutineScope,
repository = ongoingCallRepository,
+ ongoingCallInteractor = ongoingCallInteractor,
logger = statusBarChipsLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
new file mode 100644
index 000000000000..51fb36fb2ead
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.phone.ongoingcall.domain.interactor
+
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+
+val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
+ Kosmos.Fixture {
+ OngoingCallInteractor(
+ scope = applicationCoroutineScope,
+ activeNotificationsInteractor = activeNotificationsInteractor,
+ activityManagerRepository = activityManagerRepository,
+ logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
+ )
+ }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 08206150cebb..ffa259b536ec 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -221,6 +221,7 @@ java_library_static {
"securebox",
"apache-commons-math",
"battery_saver_flag_lib",
+ "guava",
"notification_flags_lib",
"power_hint_flags_lib",
"biometrics_flags_lib",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 43774bbc51ca..b0dae6a1f306 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -90,6 +90,7 @@ public abstract class PackageManagerInternal {
*/
public static final int RESOLVE_NON_RESOLVER_ONLY = 0x00000002;
+ @Deprecated
@IntDef(value = {
INTEGRITY_VERIFICATION_ALLOW,
INTEGRITY_VERIFICATION_REJECT,
@@ -97,18 +98,10 @@ public abstract class PackageManagerInternal {
@Retention(RetentionPolicy.SOURCE)
public @interface IntegrityVerificationResult {}
- /**
- * Used as the {@code verificationCode} argument for
- * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the
- * integrity component allows the install to proceed.
- */
+ @Deprecated
public static final int INTEGRITY_VERIFICATION_ALLOW = 1;
- /**
- * Used as the {@code verificationCode} argument for
- * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the
- * integrity component does not allow install to proceed.
- */
+ @Deprecated
public static final int INTEGRITY_VERIFICATION_REJECT = 0;
/**
@@ -1131,17 +1124,13 @@ public abstract class PackageManagerInternal {
public abstract boolean isPermissionUpgradeNeeded(@UserIdInt int userId);
/**
- * Allows the integrity component to respond to the
- * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
- * broadcast} to respond to the package manager. The response must include
- * the {@code verificationCode} which is one of
- * {@link #INTEGRITY_VERIFICATION_ALLOW} and {@link #INTEGRITY_VERIFICATION_REJECT}.
+ * Used to allow the integrity component to respond to the
+ * ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
+ * broadcast to respond to the package manager.
*
- * @param verificationId pending package identifier as passed via the
- * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
- * @param verificationResult either {@link #INTEGRITY_VERIFICATION_ALLOW}
- * or {@link #INTEGRITY_VERIFICATION_REJECT}.
+ * Deprecated.
*/
+ @Deprecated
public abstract void setIntegrityVerificationResult(int verificationId,
@IntegrityVerificationResult int verificationResult);
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index a132876b72a3..0914b7e3eeb2 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -93,29 +93,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
mContext = context;
mPackageManagerInternal = packageManagerInternal;
mHandler = handler;
-
- IntentFilter integrityVerificationFilter = new IntentFilter();
- integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
- try {
- integrityVerificationFilter.addDataType(PACKAGE_MIME_TYPE);
- } catch (IntentFilter.MalformedMimeTypeException e) {
- throw new RuntimeException("Mime type malformed: should never happen.", e);
- }
-
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION.equals(
- intent.getAction())) {
- return;
- }
- mHandler.post(() -> handleIntegrityVerification(intent));
- }
- },
- integrityVerificationFilter,
- /* broadcastPermission= */ null,
- mHandler);
}
@Override
@@ -157,10 +134,4 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
public List<String> getWhitelistedRuleProviders() {
return Collections.emptyList();
}
-
- private void handleIntegrityVerification(Intent intent) {
- int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- }
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 65d0ab337400..14eeb3dc78f8 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -35,6 +35,9 @@ import android.util.Log;
import com.android.server.SystemService;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -43,6 +46,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
+import java.util.UUID;
/**
* This service manage picture profile and sound profile for TV setting. Also communicates with the
@@ -54,10 +58,14 @@ public class MediaQualityService extends SystemService {
private static final String TAG = "MediaQualityService";
private final Context mContext;
private final MediaQualityDbHelper mMediaQualityDbHelper;
+ private final BiMap<Long, String> mPictureProfileTempIdMap;
+ private final BiMap<Long, String> mSoundProfileTempIdMap;
public MediaQualityService(Context context) {
super(context);
mContext = context;
+ mPictureProfileTempIdMap = HashBiMap.create();
+ mSoundProfileTempIdMap = HashBiMap.create();
mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
mMediaQualityDbHelper.setIdleConnectionTimeout(30);
@@ -80,11 +88,14 @@ public class MediaQualityService extends SystemService {
values.put(BaseParameters.PARAMETER_NAME, pp.getName());
values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
- values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
+ values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(pp.getParameters()));
// id is auto-generated by SQLite upon successful insertion of row
- long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values);
- return new PictureProfile.Builder(pp).setProfileId(Long.toString(id)).build();
+ Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+ null, values);
+ populateTempIdMap(mPictureProfileTempIdMap, id);
+ pp.setProfileId(mPictureProfileTempIdMap.get(id));
+ return pp;
}
@Override
@@ -94,26 +105,27 @@ public class MediaQualityService extends SystemService {
@Override
public void removePictureProfile(String id, int userId) {
- // TODO: implement
+ Long intId = mPictureProfileTempIdMap.inverse().get(id);
+ if (intId != null) {
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ String selection = BaseParameters.PARAMETER_ID + " = ?";
+ String[] selectionArgs = {Long.toString(intId)};
+ db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
+ selectionArgs);
+ mPictureProfileTempIdMap.remove(intId);
+ }
}
@Override
public PictureProfile getPictureProfile(int type, String name, int userId) {
- SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ BaseParameters.PARAMETER_NAME + " = ?";
String[] selectionArguments = {Integer.toString(type), name};
try (
- Cursor cursor = db.query(
+ Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- getAllPictureProfileColumns(),
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
+ getAllMediaProfileColumns(), selection, selectionArguments)
) {
int count = cursor.getCount();
if (count == 0) {
@@ -122,93 +134,19 @@ public class MediaQualityService extends SystemService {
if (count > 1) {
Log.wtf(TAG, String.format(Locale.US, "%d entries found for type=%d and name=%s"
+ " in %s. Should only ever be 0 or 1.", count, type, name,
- mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
+ mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
return null;
}
cursor.moveToFirst();
- return getPictureProfileFromCursor(cursor);
- }
- }
-
- private String bundleToJson(PersistableBundle bundle) {
- JSONObject jsonObject = new JSONObject();
- if (bundle == null) {
- return jsonObject.toString();
- }
- for (String key : bundle.keySet()) {
- try {
- jsonObject.put(key, bundle.getString(key));
- } catch (JSONException e) {
- Log.e(TAG, "Unable to serialize ", e);
- }
- }
- return jsonObject.toString();
- }
-
- private PersistableBundle jsonToBundle(String jsonString) {
- JSONObject jsonObject = null;
- PersistableBundle bundle = new PersistableBundle();
-
- try {
- jsonObject = new JSONObject(jsonString);
-
- Iterator<String> keys = jsonObject.keys();
- while (keys.hasNext()) {
- String key = keys.next();
- Object value = jsonObject.get(key);
-
- if (value instanceof String) {
- bundle.putString(key, (String) value);
- } else if (value instanceof Integer) {
- bundle.putInt(key, (Integer) value);
- } else if (value instanceof Boolean) {
- bundle.putBoolean(key, (Boolean) value);
- } else if (value instanceof Double) {
- bundle.putDouble(key, (Double) value);
- } else if (value instanceof Long) {
- bundle.putLong(key, (Long) value);
- }
- }
- } catch (JSONException e) {
- throw new RuntimeException(e);
+ return getPictureProfileWithTempIdFromCursor(cursor);
}
-
- return bundle;
- }
-
- private String[] getAllPictureProfileColumns() {
- return new String[]{
- BaseParameters.PARAMETER_ID,
- BaseParameters.PARAMETER_TYPE,
- BaseParameters.PARAMETER_NAME,
- BaseParameters.PARAMETER_INPUT_ID,
- BaseParameters.PARAMETER_PACKAGE,
- mMediaQualityDbHelper.SETTINGS
- };
- }
-
- private PictureProfile getPictureProfileFromCursor(Cursor cursor) {
- String returnId = cursor.getString(cursor.getColumnIndexOrThrow(
- BaseParameters.PARAMETER_ID));
- int type = cursor.getInt(cursor.getColumnIndexOrThrow(
- BaseParameters.PARAMETER_TYPE));
- String name = cursor.getString(cursor.getColumnIndexOrThrow(
- BaseParameters.PARAMETER_NAME));
- String inputId = cursor.getString(cursor.getColumnIndexOrThrow(
- BaseParameters.PARAMETER_INPUT_ID));
- String packageName = cursor.getString(cursor.getColumnIndexOrThrow(
- BaseParameters.PARAMETER_PACKAGE));
- String settings = cursor.getString(
- cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
- return new PictureProfile(returnId, type, name, inputId,
- packageName, jsonToBundle(settings));
}
@Override
public List<PictureProfile> getPictureProfilesByPackage(String packageName, int userId) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
- return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection,
+ return getPictureProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
selectionArguments);
}
@@ -219,36 +157,15 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getPictureProfilePackageNames(int userId) {
- String [] column = {BaseParameters.PARAMETER_NAME};
+ String [] column = {BaseParameters.PARAMETER_PACKAGE};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
return pictureProfiles.stream()
- .map(PictureProfile::getName)
+ .map(PictureProfile::getPackageName)
+ .distinct()
.collect(Collectors.toList());
}
- private List<PictureProfile> getPictureProfilesBasedOnConditions(String[] columns,
- String selection, String[] selectionArguments) {
- SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
- try (
- Cursor cursor = db.query(
- mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- columns,
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
- ) {
- List<PictureProfile> pictureProfiles = new ArrayList<>();
- while (cursor.moveToNext()) {
- pictureProfiles.add(getPictureProfileFromCursor(cursor));
- }
- return pictureProfiles;
- }
- }
-
@Override
public PictureProfileHandle getPictureProfileHandle(String id, int userId) {
return null;
@@ -259,13 +176,18 @@ public class MediaQualityService extends SystemService {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
+ values.put(BaseParameters.PARAMETER_TYPE, sp.getProfileType());
values.put(BaseParameters.PARAMETER_NAME, sp.getName());
values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName());
values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId());
- values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(sp.getParameters()));
+ values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(sp.getParameters()));
- long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
- return new SoundProfile.Builder(sp).setProfileId(Long.toString(id)).build();
+ // id is auto-generated by SQLite upon successful insertion of row
+ Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+ null, values);
+ populateTempIdMap(mSoundProfileTempIdMap, id);
+ sp.setProfileId(mSoundProfileTempIdMap.get(id));
+ return sp;
}
@Override
@@ -275,28 +197,27 @@ public class MediaQualityService extends SystemService {
@Override
public void removeSoundProfile(String id, int userId) {
- SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- String selection = BaseParameters.PARAMETER_ID + " = ?";
- String[] selectionArgs = {id};
- db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs);
+ Long intId = mSoundProfileTempIdMap.inverse().get(id);
+ if (intId != null) {
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ String selection = BaseParameters.PARAMETER_ID + " = ?";
+ String[] selectionArgs = {Long.toString(intId)};
+ db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
+ selectionArgs);
+ mSoundProfileTempIdMap.remove(intId);
+ }
}
@Override
public SoundProfile getSoundProfile(int type, String id, int userId) {
- SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
- String selection = BaseParameters.PARAMETER_ID + " = ?";
- String[] selectionArguments = {id};
+ String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ + BaseParameters.PARAMETER_NAME + " = ?";
+ String[] selectionArguments = {String.valueOf(type), id};
try (
- Cursor cursor = db.query(
+ Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
- getAllSoundProfileColumns(),
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
+ getAllMediaProfileColumns(), selection, selectionArguments)
) {
int count = cursor.getCount();
if (count == 0) {
@@ -309,7 +230,7 @@ public class MediaQualityService extends SystemService {
return null;
}
cursor.moveToFirst();
- return getSoundProfileFromCursor(cursor);
+ return getSoundProfileWithTempIdFromCursor(cursor);
}
}
@@ -317,7 +238,7 @@ public class MediaQualityService extends SystemService {
public List<SoundProfile> getSoundProfilesByPackage(String packageName, int userId) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
- return getSoundProfilesBasedOnConditions(getAllSoundProfileColumns(), selection,
+ return getSoundProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
selectionArguments);
}
@@ -332,13 +253,79 @@ public class MediaQualityService extends SystemService {
List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
null, null);
return soundProfiles.stream()
- .map(SoundProfile::getName)
+ .map(SoundProfile::getPackageName)
+ .distinct()
.collect(Collectors.toList());
}
- private String[] getAllSoundProfileColumns() {
+ private void populateTempIdMap(BiMap<Long, String> map, Long id) {
+ if (id != null && map.get(id) == null) {
+ String uuid = UUID.randomUUID().toString();
+ while (map.inverse().containsKey(uuid)) {
+ uuid = UUID.randomUUID().toString();
+ }
+ map.put(id, uuid);
+ }
+ }
+
+ private String persistableBundleToJson(PersistableBundle bundle) {
+ JSONObject json = new JSONObject();
+ for (String key : bundle.keySet()) {
+ Object value = bundle.get(key);
+ try {
+ if (value instanceof String) {
+ json.put(key, bundle.getString(key));
+ } else if (value instanceof Integer) {
+ json.put(key, bundle.getInt(key));
+ } else if (value instanceof Long) {
+ json.put(key, bundle.getLong(key));
+ } else if (value instanceof Boolean) {
+ json.put(key, bundle.getBoolean(key));
+ } else if (value instanceof Double) {
+ json.put(key, bundle.getDouble(key));
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "Unable to serialize ", e);
+ }
+ }
+ return json.toString();
+ }
+
+ private PersistableBundle jsonToBundle(String jsonString) {
+ PersistableBundle bundle = new PersistableBundle();
+ if (jsonString != null) {
+ JSONObject jsonObject = null;
+ try {
+ jsonObject = new JSONObject(jsonString);
+
+ Iterator<String> keys = jsonObject.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ Object value = jsonObject.get(key);
+
+ if (value instanceof String) {
+ bundle.putString(key, (String) value);
+ } else if (value instanceof Integer) {
+ bundle.putInt(key, (Integer) value);
+ } else if (value instanceof Boolean) {
+ bundle.putBoolean(key, (Boolean) value);
+ } else if (value instanceof Double) {
+ bundle.putDouble(key, (Double) value);
+ } else if (value instanceof Long) {
+ bundle.putLong(key, (Long) value);
+ }
+ }
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return bundle;
+ }
+
+ private String[] getAllMediaProfileColumns() {
return new String[]{
BaseParameters.PARAMETER_ID,
+ BaseParameters.PARAMETER_TYPE,
BaseParameters.PARAMETER_NAME,
BaseParameters.PARAMETER_INPUT_ID,
BaseParameters.PARAMETER_PACKAGE,
@@ -346,40 +333,92 @@ public class MediaQualityService extends SystemService {
};
}
- private SoundProfile getSoundProfileFromCursor(Cursor cursor) {
- String returnId = cursor.getString(
- cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_ID));
- int type = cursor.getInt(
- cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_TYPE));
- String name = cursor.getString(
- cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_NAME));
- String inputId = cursor.getString(
- cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_INPUT_ID));
- String packageName = cursor.getString(
- cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_PACKAGE));
- String settings = cursor.getString(
- cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
- return new SoundProfile(returnId, type, name, inputId, packageName,
- jsonToBundle(settings));
+ private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) {
+ return new PictureProfile(
+ getTempId(mPictureProfileTempIdMap, cursor),
+ getType(cursor),
+ getName(cursor),
+ getInputId(cursor),
+ getPackageName(cursor),
+ jsonToBundle(getSettingsString(cursor))
+ );
}
- private List<SoundProfile> getSoundProfilesBasedOnConditions(String[] columns,
- String selection, String[] selectionArguments) {
+ private SoundProfile getSoundProfileWithTempIdFromCursor(Cursor cursor) {
+ return new SoundProfile(
+ getTempId(mSoundProfileTempIdMap, cursor),
+ getType(cursor),
+ getName(cursor),
+ getInputId(cursor),
+ getPackageName(cursor),
+ jsonToBundle(getSettingsString(cursor))
+ );
+ }
+
+ private String getTempId(BiMap<Long, String> map, Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
+ Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
+ populateTempIdMap(map, dbId);
+ return map.get(dbId);
+ }
+
+ private int getType(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE);
+ return colIndex != -1 ? cursor.getInt(colIndex) : 0;
+ }
+
+ private String getName(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private String getInputId(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private String getPackageName(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private String getSettingsString(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private Cursor getCursorAfterQuerying(String table, String[] columns, String selection,
+ String[] selectionArgs) {
SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
+ return db.query(table, columns, selection, selectionArgs,
+ /*groupBy=*/ null, /*having=*/ null, /*orderBy=*/ null);
+ }
+ private List<PictureProfile> getPictureProfilesBasedOnConditions(String[] columns,
+ String selection, String[] selectionArguments) {
try (
- Cursor cursor = db.query(
- mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
- columns,
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
+ Cursor cursor = getCursorAfterQuerying(
+ mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, columns, selection,
+ selectionArguments)
+ ) {
+ List<PictureProfile> pictureProfiles = new ArrayList<>();
+ while (cursor.moveToNext()) {
+ pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor));
+ }
+ return pictureProfiles;
+ }
+ }
+
+ private List<SoundProfile> getSoundProfilesBasedOnConditions(String[] columns,
+ String selection, String[] selectionArguments) {
+ try (
+ Cursor cursor = getCursorAfterQuerying(
+ mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, columns, selection,
+ selectionArguments)
) {
List<SoundProfile> soundProfiles = new ArrayList<>();
while (cursor.moveToNext()) {
- soundProfiles.add(getSoundProfileFromCursor(cursor));
+ soundProfiles.add(getSoundProfileWithTempIdFromCursor(cursor));
}
return soundProfiles;
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 0a0882d80cc1..4ea405441030 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -18,7 +18,6 @@ package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
@@ -29,7 +28,6 @@ import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
import static com.android.server.pm.PackageManagerService.INSTANT_APP_RESOLUTION_PHASE_TWO;
-import static com.android.server.pm.PackageManagerService.INTEGRITY_VERIFICATION_COMPLETE;
import static com.android.server.pm.PackageManagerService.PACKAGE_VERIFIED;
import static com.android.server.pm.PackageManagerService.POST_INSTALL;
import static com.android.server.pm.PackageManagerService.PRUNE_UNUSED_STATIC_SHARED_LIBRARIES;
@@ -149,42 +147,6 @@ final class PackageHandler extends Handler {
break;
}
- case CHECK_PENDING_INTEGRITY_VERIFICATION: {
- final int verificationId = msg.arg1;
- final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
-
- if (state != null && !state.isIntegrityVerificationComplete()) {
- final VerifyingSession verifyingSession = state.getVerifyingSession();
- final Uri originUri = Uri.fromFile(verifyingSession.mOriginInfo.mResolvedFile);
-
- String errorMsg = "Integrity verification timed out for " + originUri;
- Slog.i(TAG, errorMsg);
-
- state.setIntegrityVerificationResult(
- getDefaultIntegrityVerificationResponse());
-
- if (getDefaultIntegrityVerificationResponse()
- == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
- Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
- } else {
- verifyingSession.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- errorMsg);
- }
-
- if (state.areAllVerificationsComplete()) {
- mPm.mPendingVerification.remove(verificationId);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER,
- "integrity_verification",
- verificationId);
-
- verifyingSession.handleIntegrityVerificationFinished();
- }
- break;
- }
case PACKAGE_VERIFIED: {
final int verificationId = msg.arg1;
@@ -205,42 +167,6 @@ final class PackageHandler extends Handler {
break;
}
- case INTEGRITY_VERIFICATION_COMPLETE: {
- final int verificationId = msg.arg1;
-
- final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
- if (state == null) {
- Slog.w(TAG, "Integrity verification with id " + verificationId
- + " not found. It may be invalid or overridden by verifier");
- break;
- }
-
- final int response = (Integer) msg.obj;
- final VerifyingSession verifyingSession = state.getVerifyingSession();
- final Uri originUri = Uri.fromFile(verifyingSession.mOriginInfo.mResolvedFile);
-
- state.setIntegrityVerificationResult(response);
-
- if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
- Slog.i(TAG, "Integrity check passed for " + originUri);
- } else {
- verifyingSession.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Integrity check failed for " + originUri);
- }
-
- if (state.areAllVerificationsComplete()) {
- mPm.mPendingVerification.remove(verificationId);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER,
- "integrity_verification",
- verificationId);
-
- verifyingSession.handleIntegrityVerificationFinished();
- break;
- }
case INSTANT_APP_RESOLUTION_PHASE_TWO: {
InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
mPm.snapshotComputer(),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ab26f024a18a..65bb701563a8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -923,8 +923,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
static final int ENABLE_ROLLBACK_TIMEOUT = 22;
static final int DEFERRED_NO_KILL_POST_DELETE = 23;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
- static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
- static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
+ // static final int UNUSED = 25;
+ // static final int UNUSED = 26;
static final int DOMAIN_VERIFICATION = 27;
static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;
@@ -7124,12 +7124,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return mSettings.isPermissionUpgradeNeeded(userId);
}
+ @Deprecated
@Override
public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
- final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE);
- msg.arg1 = verificationId;
- msg.obj = verificationResult;
- mHandler.sendMessage(msg);
+ // Do nothing.
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index 0b6ccc41d956..63c2ee2e5454 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -43,8 +43,6 @@ class PackageVerificationState {
private boolean mRequiredVerificationPassed;
- private boolean mIntegrityVerificationComplete;
-
/**
* Create a new package verification state where {@code requiredVerifierUid} is the user ID for
* the package that must reply affirmative before things can continue.
@@ -213,15 +211,7 @@ class PackageVerificationState {
return mExtendedTimeoutUids.get(uid, false);
}
- void setIntegrityVerificationResult(int code) {
- mIntegrityVerificationComplete = true;
- }
-
- boolean isIntegrityVerificationComplete() {
- return mIntegrityVerificationComplete;
- }
-
boolean areAllVerificationsComplete() {
- return mIntegrityVerificationComplete && isVerificationComplete();
+ return isVerificationComplete();
}
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index f7eb29fe3ee9..542ae8eb9207 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -28,7 +28,6 @@ import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
@@ -87,11 +86,6 @@ final class VerifyingSession {
* Whether verification is enabled by default.
*/
private static final boolean DEFAULT_VERIFY_ENABLE = true;
-
- /**
- * Whether integrity verification is enabled by default.
- */
- private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true;
/**
* The default maximum time to wait for the integrity verification to return in
* milliseconds.
@@ -129,7 +123,6 @@ final class VerifyingSession {
private final boolean mUserActionRequired;
private final int mUserActionRequiredType;
private boolean mWaitForVerificationToComplete;
- private boolean mWaitForIntegrityVerificationToComplete;
private boolean mWaitForEnableRollbackToComplete;
private int mRet = PackageManager.INSTALL_SUCCEEDED;
private String mErrorMessage = null;
@@ -217,7 +210,6 @@ final class VerifyingSession {
new PackageVerificationState(this);
mPm.mPendingVerification.append(verificationId, verificationState);
- sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
sendPackageVerificationRequest(
verificationId, pkgLite, verificationState);
@@ -270,89 +262,6 @@ final class VerifyingSession {
mPm.mHandler.sendMessageDelayed(msg, rollbackTimeout);
}
- /**
- * Send a request to check the integrity of the package.
- */
- void sendIntegrityVerificationRequest(
- int verificationId,
- PackageInfoLite pkgLite,
- PackageVerificationState verificationState) {
- if (!isIntegrityVerificationEnabled()) {
- // Consider the integrity check as passed.
- verificationState.setIntegrityVerificationResult(
- PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- return;
- }
-
- final Intent integrityVerification =
- new Intent(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
-
- integrityVerification.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
- PACKAGE_MIME_TYPE);
-
- final int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND;
- integrityVerification.addFlags(flags);
-
- integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId);
- integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName);
- integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode);
- integrityVerification.putExtra(EXTRA_LONG_VERSION_CODE, pkgLite.getLongVersionCode());
- populateInstallerExtras(integrityVerification);
-
- // send to integrity component only.
- integrityVerification.setPackage("android");
-
- final BroadcastOptions options = BroadcastOptions.makeBasic();
-
- mPm.mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
- /* receiverPermission= */ null,
- /* appOp= */ AppOpsManager.OP_NONE,
- /* options= */ options.toBundle(),
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Message msg =
- mPm.mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION);
- msg.arg1 = verificationId;
- mPm.mHandler.sendMessageDelayed(msg, getIntegrityVerificationTimeout());
- }
- }, /* scheduler= */ null,
- /* initialCode= */ 0,
- /* initialData= */ null,
- /* initialExtras= */ null);
-
- Trace.asyncTraceBegin(
- TRACE_TAG_PACKAGE_MANAGER, "integrity_verification", verificationId);
-
- // stop the copy until verification succeeds.
- mWaitForIntegrityVerificationToComplete = true;
- }
-
-
- /**
- * Get the integrity verification timeout.
- *
- * @return verification timeout in milliseconds
- */
- private long getIntegrityVerificationTimeout() {
- long timeout = Settings.Global.getLong(mPm.mContext.getContentResolver(),
- Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
- DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
- // The setting can be used to increase the timeout but not decrease it, since that is
- // equivalent to disabling the integrity component.
- return Math.max(timeout, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
- }
-
- /**
- * Check whether or not integrity verification has been enabled.
- */
- private boolean isIntegrityVerificationEnabled() {
- // We are not exposing this as a user-configurable setting because we don't want to provide
- // an easy way to get around the integrity check.
- return DEFAULT_INTEGRITY_VERIFY_ENABLE;
- }
/**
* Send a request to verifier(s) to verify the package if necessary.
@@ -827,11 +736,6 @@ final class VerifyingSession {
handleReturnCode();
}
- void handleIntegrityVerificationFinished() {
- mWaitForIntegrityVerificationToComplete = false;
- handleReturnCode();
- }
-
void handleRollbackEnabled() {
// TODO(b/112431924): Consider halting the install if we
// couldn't enable rollback.
@@ -840,7 +744,7 @@ final class VerifyingSession {
}
void handleReturnCode() {
- if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
+ if (mWaitForVerificationToComplete
|| mWaitForEnableRollbackToComplete) {
return;
}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 0f6688f73c22..f1f1e6031718 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -41,6 +41,7 @@ import android.hardware.power.GpuHeadroomResult;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
+import android.hardware.power.SupportInfo;
import android.hardware.power.WorkDuration;
import android.os.Binder;
import android.os.CpuHeadroomParamsInternal;
@@ -103,7 +104,6 @@ public final class HintManagerService extends SystemService {
// The minimum interval between the headroom calls as rate limiting.
private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000;
private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000;
- private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1;
@VisibleForTesting final long mHintSessionPreferredRate;
@@ -181,6 +181,7 @@ public final class HintManagerService extends SystemService {
private final IPower mPowerHal;
private int mPowerHalVersion;
+ private SupportInfo mSupportInfo = null;
private final PackageManager mPackageManager;
private boolean mUsesFmq;
@@ -248,13 +249,11 @@ public final class HintManagerService extends SystemService {
@GuardedBy("mCpuHeadroomLock")
private final HeadroomCache<CpuHeadroomParams, CpuHeadroomResult> mCpuHeadroomCache;
- private final long mCpuHeadroomIntervalMillis;
private final Object mGpuHeadroomLock = new Object();
@GuardedBy("mGpuHeadroomLock")
private final HeadroomCache<GpuHeadroomParams, GpuHeadroomResult> mGpuHeadroomCache;
- private final long mGpuHeadroomIntervalMillis;
// these are set to default values in CpuHeadroomParamsInternal and GpuHeadroomParamsInternal
private final int mDefaultCpuHeadroomCalculationWindowMillis;
@@ -296,79 +295,70 @@ public final class HintManagerService extends SystemService {
mPowerHal = injector.createIPower();
mPowerHalVersion = 0;
mUsesFmq = false;
- long cpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
- long gpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
if (mPowerHal != null) {
- try {
- mPowerHalVersion = mPowerHal.getInterfaceVersion();
- if (mPowerHal.getInterfaceVersion() >= 6) {
- if (SystemProperties.getBoolean(PROPERTY_USE_HAL_HEADROOMS, true)) {
- cpuHeadroomIntervalMillis = checkCpuHeadroomSupport();
- gpuHeadroomIntervalMillis = checkGpuHeadroomSupport();
- }
- }
- } catch (RemoteException e) {
- throw new IllegalStateException("Could not contact PowerHAL!", e);
- }
+ mSupportInfo = getSupportInfo();
}
- mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis;
mDefaultCpuHeadroomCalculationWindowMillis =
new CpuHeadroomParamsInternal().calculationWindowMillis;
mDefaultGpuHeadroomCalculationWindowMillis =
new GpuHeadroomParamsInternal().calculationWindowMillis;
- mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis;
- if (mCpuHeadroomIntervalMillis > 0) {
- mCpuHeadroomCache = new HeadroomCache<>(2, mCpuHeadroomIntervalMillis);
+ if (mSupportInfo.headroom.isCpuSupported) {
+ mCpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.cpuMinIntervalMillis);
} else {
mCpuHeadroomCache = null;
}
- if (mGpuHeadroomIntervalMillis > 0) {
- mGpuHeadroomCache = new HeadroomCache<>(2, mGpuHeadroomIntervalMillis);
+ if (mSupportInfo.headroom.isGpuSupported) {
+ mGpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.gpuMinIntervalMillis);
} else {
mGpuHeadroomCache = null;
}
}
- private long checkCpuHeadroomSupport() {
- final CpuHeadroomParams params = new CpuHeadroomParams();
- params.tids = new int[]{Process.myPid()};
- try {
- synchronized (mCpuHeadroomLock) {
- final CpuHeadroomResult ret = mPowerHal.getCpuHeadroom(params);
- if (ret != null && ret.getTag() == CpuHeadroomResult.globalHeadroom
- && !Float.isNaN(ret.getGlobalHeadroom())) {
- return Math.max(
- DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS,
- mPowerHal.getCpuHeadroomMinIntervalMillis());
- }
- }
-
- } catch (UnsupportedOperationException e) {
- Slog.w(TAG, "getCpuHeadroom HAL API is not supported, params: " + params, e);
- } catch (RemoteException e) {
- Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API, params: " + params, e);
- }
- return HEADROOM_INTERVAL_UNSUPPORTED;
- }
-
- private long checkGpuHeadroomSupport() {
- final GpuHeadroomParams params = new GpuHeadroomParams();
+ SupportInfo getSupportInfo() {
try {
- synchronized (mGpuHeadroomLock) {
- final GpuHeadroomResult ret = mPowerHal.getGpuHeadroom(params);
- if (ret != null && ret.getTag() == GpuHeadroomResult.globalHeadroom && !Float.isNaN(
- ret.getGlobalHeadroom())) {
- return Math.max(
- DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS,
- mPowerHal.getGpuHeadroomMinIntervalMillis());
- }
+ mPowerHalVersion = mPowerHal.getInterfaceVersion();
+ if (mPowerHalVersion >= 6) {
+ return mPowerHal.getSupportInfo();
}
- } catch (UnsupportedOperationException e) {
- Slog.w(TAG, "getGpuHeadroom HAL API is not supported, params: " + params, e);
} catch (RemoteException e) {
- Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API, params: " + params, e);
- }
- return HEADROOM_INTERVAL_UNSUPPORTED;
+ throw new IllegalStateException("Could not contact PowerHAL!", e);
+ }
+
+ SupportInfo supportInfo = new SupportInfo();
+ supportInfo.usesSessions = isHintSessionSupported();
+ // Global boosts & modes aren't currently relevant for HMS clients
+ supportInfo.boosts = 0;
+ supportInfo.modes = 0;
+ supportInfo.sessionHints = 0;
+ supportInfo.sessionModes = 0;
+ supportInfo.sessionTags = 0;
+ if (isHintSessionSupported()) {
+ if (mPowerHalVersion == 4) {
+ // Assume we support the V4 hints & modes unless specified
+ // otherwise; this is to avoid breaking backwards compat
+ // since we historically just assumed they were.
+ supportInfo.sessionHints = 31; // first 5 bits are ones
+ }
+ if (mPowerHalVersion == 5) {
+ // Assume we support the V5 hints & modes unless specified
+ // otherwise; this is to avoid breaking backwards compat
+ // since we historically just assumed they were.
+
+ // Hal V5 has 8 modes, all of which it assumes are supported,
+ // so we represent that by having the first 8 bits set
+ supportInfo.sessionHints = 255; // first 8 bits are ones
+ // Hal V5 has 1 mode which it assumes is supported, so we
+ // represent that by having the first bit set
+ supportInfo.sessionModes = 1;
+ // Hal V5 has 5 tags, all of which it assumes are supported,
+ // so we represent that by having the first 5 bits set
+ supportInfo.sessionTags = 31;
+ }
+ }
+ supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+ supportInfo.headroom.isCpuSupported = false;
+ supportInfo.headroom.isGpuSupported = false;
+ return supportInfo;
}
private ServiceThread createCleanUpThread() {
@@ -557,7 +547,7 @@ public final class HintManagerService extends SystemService {
return targetDurations;
}
}
- private boolean isHalSupported() {
+ private boolean isHintSessionSupported() {
return mHintSessionPreferredRate != -1;
}
@@ -1267,7 +1257,7 @@ public final class HintManagerService extends SystemService {
public IHintSession createHintSessionWithConfig(@NonNull IBinder token,
@SessionTag int tag, SessionCreationConfig creationConfig,
SessionConfig config) {
- if (!isHalSupported()) {
+ if (!isHintSessionSupported()) {
throw new UnsupportedOperationException("PowerHAL is not supported!");
}
@@ -1488,14 +1478,13 @@ public final class HintManagerService extends SystemService {
@Override
public CpuHeadroomResult getCpuHeadroom(@NonNull CpuHeadroomParamsInternal params) {
- if (mCpuHeadroomIntervalMillis <= 0) {
+ if (!mSupportInfo.headroom.isCpuSupported) {
throw new UnsupportedOperationException();
}
final CpuHeadroomParams halParams = new CpuHeadroomParams();
halParams.tids = new int[]{Binder.getCallingPid()};
halParams.calculationType = params.calculationType;
halParams.calculationWindowMillis = params.calculationWindowMillis;
- halParams.selectionType = params.selectionType;
if (params.usesDeviceHeadroom) {
halParams.tids = new int[]{};
} else if (params.tids != null && params.tids.length > 0) {
@@ -1544,7 +1533,7 @@ public final class HintManagerService extends SystemService {
@Override
public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) {
- if (mGpuHeadroomIntervalMillis <= 0) {
+ if (!mSupportInfo.headroom.isGpuSupported) {
throw new UnsupportedOperationException();
}
final GpuHeadroomParams halParams = new GpuHeadroomParams();
@@ -1579,18 +1568,18 @@ public final class HintManagerService extends SystemService {
@Override
public long getCpuHeadroomMinIntervalMillis() {
- if (mCpuHeadroomIntervalMillis <= 0) {
+ if (!mSupportInfo.headroom.isCpuSupported) {
throw new UnsupportedOperationException();
}
- return mCpuHeadroomIntervalMillis;
+ return mSupportInfo.headroom.cpuMinIntervalMillis;
}
@Override
public long getGpuHeadroomMinIntervalMillis() {
- if (mGpuHeadroomIntervalMillis <= 0) {
+ if (!mSupportInfo.headroom.isGpuSupported) {
throw new UnsupportedOperationException();
}
- return mGpuHeadroomIntervalMillis;
+ return mSupportInfo.headroom.gpuMinIntervalMillis;
}
@Override
@@ -1609,7 +1598,7 @@ public final class HintManagerService extends SystemService {
}
pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate);
pw.println("MaxGraphicsPipelineThreadsCount: " + MAX_GRAPHICS_PIPELINE_THREADS_COUNT);
- pw.println("HAL Support: " + isHalSupported());
+ pw.println("HAL Support: " + isHintSessionSupported());
pw.println("Active Sessions:");
synchronized (mLock) {
for (int i = 0; i < mActiveSessions.size(); i++) {
@@ -1625,20 +1614,13 @@ public final class HintManagerService extends SystemService {
}
}
}
- pw.println("CPU Headroom Interval: " + mCpuHeadroomIntervalMillis);
- pw.println("GPU Headroom Interval: " + mGpuHeadroomIntervalMillis);
+ pw.println("CPU Headroom Interval: " + mSupportInfo.headroom.cpuMinIntervalMillis);
+ pw.println("GPU Headroom Interval: " + mSupportInfo.headroom.gpuMinIntervalMillis);
try {
CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
- params.selectionType = CpuHeadroomParams.SelectionType.ALL;
params.usesDeviceHeadroom = true;
CpuHeadroomResult ret = getCpuHeadroom(params);
pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
- params = new CpuHeadroomParamsInternal();
- params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
- params.usesDeviceHeadroom = true;
- ret = getCpuHeadroom(params);
- pw.println("CPU headroom per core: " + (ret == null ? "N/A"
- : Arrays.toString(ret.getPerCoreHeadroom())));
} catch (Exception e) {
Slog.d(TAG, "Failed to dump CPU headroom", e);
pw.println("CPU headroom: N/A");
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 15c3099511f9..1b6ce9dacfa9 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -119,6 +119,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void finishSession() {
+ Slog.d(TAG, "Session finish requested, ending vibration session...");
// Do not abort session in HAL, wait for ongoing vibration requests to complete.
// This might take a while to end the session, but it can be aborted by cancelSession.
requestEndSession(Status.FINISHED, /* shouldAbort= */ false, /* isVendorRequest= */ true);
@@ -126,6 +127,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void cancelSession() {
+ Slog.d(TAG, "Session cancel requested, aborting vibration session...");
// Always abort session in HAL while cancelling it.
// This might be triggered after finishSession was already called.
requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true,
@@ -228,13 +230,14 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void notifySessionCallback() {
synchronized (mLock) {
+ Slog.d(TAG, "Session callback received, ending vibration session...");
// If end was not requested then the HAL has cancelled the session.
maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON,
/* isVendorRequest= */ false);
maybeSetStatusToRequestedLocked();
clearVibrationConductor();
+ mHandler.post(() -> mManagerHooks.onSessionReleased(mSessionId));
}
- mHandler.post(() -> mManagerHooks.onSessionReleased(mSessionId));
}
@Override
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java
index a93e8ad93756..97f1bd46678f 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -574,57 +574,16 @@ public class PackageVerificationStateTest extends AndroidTestCase {
assertTrue(state.isInstallAllowed());
}
- public void testAreAllVerificationsComplete_onlyVerificationPasses() {
+ public void testAreAllVerificationsComplete() {
PackageVerificationState state = new PackageVerificationState(null);
state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
- assertFalse(state.areAllVerificationsComplete());
- }
-
- public void testAreAllVerificationsComplete_onlyIntegrityCheckPasses() {
- PackageVerificationState state = new PackageVerificationState(null);
- state.addRequiredVerifierUid(REQUIRED_UID_1);
- assertFalse(state.areAllVerificationsComplete());
-
- state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-
- assertFalse(state.areAllVerificationsComplete());
- }
-
- public void testAreAllVerificationsComplete_bothPasses() {
- PackageVerificationState state = new PackageVerificationState(null);
- state.addRequiredVerifierUid(REQUIRED_UID_1);
- assertFalse(state.areAllVerificationsComplete());
-
- state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
-
assertTrue(state.areAllVerificationsComplete());
}
- public void testAreAllVerificationsComplete_onlyVerificationFails() {
- PackageVerificationState state = new PackageVerificationState(null);
- state.addRequiredVerifierUid(REQUIRED_UID_1);
- assertFalse(state.areAllVerificationsComplete());
-
- state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
-
- assertFalse(state.areAllVerificationsComplete());
- }
-
- public void testAreAllVerificationsComplete_onlyIntegrityCheckFails() {
- PackageVerificationState state = new PackageVerificationState(null);
- state.addRequiredVerifierUid(REQUIRED_UID_1);
- assertFalse(state.areAllVerificationsComplete());
-
- state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
-
- assertFalse(state.areAllVerificationsComplete());
- }
-
private void processOnTimeout(PackageVerificationState state, int code, int uid) {
// CHECK_PENDING_VERIFICATION handler.
assertFalse("Verification should not be marked as complete yet",
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 313c01d5ce58..a9b4ca1e7963 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -58,6 +58,7 @@ import android.hardware.power.GpuHeadroomResult;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
+import android.hardware.power.SupportInfo;
import android.hardware.power.WorkDuration;
import android.os.Binder;
import android.os.CpuHeadroomParamsInternal;
@@ -159,6 +160,7 @@ public class HintManagerServiceTest {
private HintManagerService mService;
private ChannelConfig mConfig;
+ private SupportInfo mSupportInfo;
private static Answer<Long> fakeCreateWithConfig(Long ptr, Long sessionId) {
return new Answer<Long>() {
@@ -179,6 +181,18 @@ public class HintManagerServiceTest {
mConfig.eventFlagDescriptor = new MQDescriptor<Byte, Byte>();
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
+ mSupportInfo = new SupportInfo();
+ mSupportInfo.usesSessions = true;
+ mSupportInfo.sessionHints = 5;
+ mSupportInfo.sessionModes = 1;
+ mSupportInfo.modes = 3;
+ mSupportInfo.boosts = 3;
+ mSupportInfo.sessionTags = 63;
+ mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+ mSupportInfo.headroom.isCpuSupported = true;
+ mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
+ mSupportInfo.headroom.isGpuSupported = true;
+ mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getNameForUid(anyInt())).thenReturn(TEST_APP_NAME);
when(mMockPackageManager.getApplicationInfo(eq(TEST_APP_NAME), anyInt()))
@@ -205,6 +219,7 @@ public class HintManagerServiceTest {
SESSION_IDS[2]));
when(mIPowerMock.getInterfaceVersion()).thenReturn(6);
+ when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo);
when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
@@ -1247,28 +1262,22 @@ public class HintManagerServiceTest {
@Test
public void testCpuHeadroomCache() throws Exception {
- when(mIPowerMock.getCpuHeadroomMinIntervalMillis()).thenReturn(2000L);
CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
CpuHeadroomParams halParams1 = new CpuHeadroomParams();
halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN;
- halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL;
halParams1.tids = new int[]{Process.myPid()};
CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal();
params2.usesDeviceHeadroom = true;
params2.calculationType = CpuHeadroomParams.CalculationType.MIN;
- params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
CpuHeadroomParams halParams2 = new CpuHeadroomParams();
halParams2.calculationType = CpuHeadroomParams.CalculationType.MIN;
- halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
halParams2.tids = new int[]{};
CpuHeadroomParamsInternal params3 = new CpuHeadroomParamsInternal();
params3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
- params3.selectionType = CpuHeadroomParams.SelectionType.ALL;
CpuHeadroomParams halParams3 = new CpuHeadroomParams();
halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
- halParams3.selectionType = CpuHeadroomParams.SelectionType.ALL;
halParams3.tids = new int[]{Process.myPid()};
// this params should not be cached as the window is not default
@@ -1276,15 +1285,14 @@ public class HintManagerServiceTest {
params4.calculationWindowMillis = 123;
CpuHeadroomParams halParams4 = new CpuHeadroomParams();
halParams4.calculationType = CpuHeadroomParams.CalculationType.MIN;
- halParams4.selectionType = CpuHeadroomParams.SelectionType.ALL;
halParams4.calculationWindowMillis = 123;
halParams4.tids = new int[]{Process.myPid()};
float headroom1 = 0.1f;
CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1);
when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1);
- float[] headroom2 = new float[] {0.2f, 0.2f};
- CpuHeadroomResult halRet2 = CpuHeadroomResult.perCoreHeadroom(headroom2);
+ float headroom2 = 0.2f;
+ CpuHeadroomResult halRet2 = CpuHeadroomResult.globalHeadroom(headroom2);
when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(halRet2);
float headroom3 = 0.3f;
CpuHeadroomResult halRet3 = CpuHeadroomResult.globalHeadroom(headroom3);
@@ -1296,8 +1304,6 @@ public class HintManagerServiceTest {
HintManagerService service = createService();
clearInvocations(mIPowerMock);
- service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
- verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis();
assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
@@ -1348,7 +1354,6 @@ public class HintManagerServiceTest {
@Test
public void testGpuHeadroomCache() throws Exception {
- when(mIPowerMock.getGpuHeadroomMinIntervalMillis()).thenReturn(2000L);
GpuHeadroomParamsInternal params1 = new GpuHeadroomParamsInternal();
GpuHeadroomParams halParams1 = new GpuHeadroomParams();
halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN;
@@ -1369,8 +1374,6 @@ public class HintManagerServiceTest {
HintManagerService service = createService();
clearInvocations(mIPowerMock);
- service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
- verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis();
assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
verify(mIPowerMock, times(2)).getGpuHeadroom(any());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 9cd3186f99f3..605fed09dd9f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -92,6 +93,8 @@ public class AuthServiceTest {
private static final String TEST_OP_PACKAGE_NAME = "test_package";
+ private final @UserIdInt int mUserId = UserHandle.getCallingUserId();
+
private AuthService mAuthService;
@Rule
@@ -257,12 +260,11 @@ public class AuthServiceTest {
final Binder token = new Binder();
final PromptInfo promptInfo = new PromptInfo();
final long sessionId = 0;
- final int userId = 0;
mAuthService.mImpl.authenticate(
token,
sessionId,
- userId,
+ mUserId,
mReceiver,
TEST_OP_PACKAGE_NAME,
promptInfo);
@@ -270,7 +272,7 @@ public class AuthServiceTest {
verify(mBiometricService).authenticate(
eq(token),
eq(sessionId),
- eq(userId),
+ eq(mUserId),
eq(mReceiver),
eq(TEST_OP_PACKAGE_NAME),
eq(promptInfo));
@@ -286,12 +288,11 @@ public class AuthServiceTest {
final Binder token = new Binder();
final PromptInfo promptInfo = new PromptInfo();
final long sessionId = 0;
- final int userId = 0;
mAuthService.mImpl.authenticate(
token,
sessionId,
- userId,
+ mUserId,
mReceiver,
TEST_OP_PACKAGE_NAME,
promptInfo);
@@ -299,7 +300,7 @@ public class AuthServiceTest {
verify(mBiometricService, never()).authenticate(
eq(token),
eq(sessionId),
- eq(userId),
+ eq(mUserId),
eq(mReceiver),
eq(TEST_OP_PACKAGE_NAME),
eq(promptInfo));
@@ -313,12 +314,11 @@ public class AuthServiceTest {
final PromptInfo promptInfo = new PromptInfo();
final long sessionId = 0;
- final int userId = 0;
mAuthService.mImpl.authenticate(
null /* token */,
sessionId,
- userId,
+ mUserId,
mReceiver,
TEST_OP_PACKAGE_NAME,
promptInfo);
@@ -338,7 +338,7 @@ public class AuthServiceTest {
mAuthService.mImpl.authenticate(
token,
0, /* sessionId */
- 0, /* userId */
+ mUserId,
mReceiver,
TEST_OP_PACKAGE_NAME,
new PromptInfo());
@@ -356,7 +356,7 @@ public class AuthServiceTest {
mAuthService.mImpl.authenticate(
token,
0, /* sessionId */
- 0, /* userId */
+ mUserId,
mReceiver,
TEST_OP_PACKAGE_NAME,
new PromptInfo());
@@ -414,20 +414,19 @@ public class AuthServiceTest {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
- final int userId = 0;
final int expectedResult = BIOMETRIC_SUCCESS;
final int authenticators = 0;
when(mBiometricService.canAuthenticate(anyString(), anyInt(), anyInt(), anyInt()))
.thenReturn(expectedResult);
final int result = mAuthService.mImpl
- .canAuthenticate(TEST_OP_PACKAGE_NAME, userId, authenticators);
+ .canAuthenticate(TEST_OP_PACKAGE_NAME, mUserId, authenticators);
assertEquals(expectedResult, result);
waitForIdle();
verify(mBiometricService).canAuthenticate(
eq(TEST_OP_PACKAGE_NAME),
- eq(userId),
+ eq(mUserId),
eq(UserHandle.getCallingUserId()),
eq(authenticators));
}
@@ -440,18 +439,17 @@ public class AuthServiceTest {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
- final int userId = 0;
final boolean expectedResult = true;
when(mBiometricService.hasEnrolledBiometrics(anyInt(), anyString())).thenReturn(
expectedResult);
- final boolean result = mAuthService.mImpl.hasEnrolledBiometrics(userId,
+ final boolean result = mAuthService.mImpl.hasEnrolledBiometrics(mUserId,
TEST_OP_PACKAGE_NAME);
assertEquals(expectedResult, result);
waitForIdle();
verify(mBiometricService).hasEnrolledBiometrics(
- eq(userId),
+ eq(mUserId),
eq(TEST_OP_PACKAGE_NAME));
}
@@ -528,13 +526,12 @@ public class AuthServiceTest {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
- final int userId = 0;
final int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG;
- mAuthService.mImpl.getLastAuthenticationTime(userId, authenticators);
+ mAuthService.mImpl.getLastAuthenticationTime(mUserId, authenticators);
waitForIdle();
- verify(mBiometricService).getLastAuthenticationTime(eq(userId), eq(authenticators));
+ verify(mBiometricService).getLastAuthenticationTime(eq(mUserId), eq(authenticators));
}
private static void setInternalAndTestBiometricPermissions(
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
deleted file mode 100644
index fd221185bacf..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2019 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.server.integrity;
-
-import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
-import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
-import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
-import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Settings;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.R;
-import com.android.server.compat.PlatformCompat;
-import com.android.server.testutils.TestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
-@RunWith(JUnit4.class)
-public class AppIntegrityManagerServiceImplTest {
- private static final String TEST_APP_PATH =
- "AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk";
-
- private static final String TEST_APP_TWO_CERT_PATH =
- "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
-
- private static final String TEST_APP_SOURCE_STAMP_PATH =
- "AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk";
-
- private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
- private static final String VERSION = "version";
- private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
-
- private static final String PACKAGE_NAME = "com.test.app";
-
- private static final long VERSION_CODE = 100;
- private static final String INSTALLER = "com.long.random.test.installer.name";
-
- // These are obtained by running the test and checking logcat.
- private static final String APP_CERT =
- "F14CFECF5070874C05D3D2FA98E046BE20BDE02A0DC74BAF6B59C6A0E4C06850";
- // We use SHA256 for package names longer than 32 characters.
- private static final String INSTALLER_SHA256 =
- "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
- private static final String SOURCE_STAMP_CERTIFICATE_HASH =
- "C6E737809CEF2B08CC6694892215F82A5E8FBC3C2A0F6212770310B90622D2D9";
-
- private static final String DUMMY_APP_TWO_CERTS_CERT_1 =
- "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94";
- private static final String DUMMY_APP_TWO_CERTS_CERT_2 =
- "94366E0A80F3A3F0D8171A15760B88E228CD6E1101F0414C98878724FBE70147";
-
- private static final String PLAY_STORE_PKG = "com.android.vending";
- private static final String ADB_INSTALLER = "adb";
- private static final String PLAY_STORE_CERT = "play_store_cert";
-
- @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock PackageManagerInternal mPackageManagerInternal;
- @Mock PlatformCompat mPlatformCompat;
- @Mock Context mMockContext;
- @Mock Resources mMockResources;
- @Mock Handler mHandler;
-
- private final Context mRealContext = InstrumentationRegistry.getTargetContext();
-
- private PackageManager mSpyPackageManager;
- private File mTestApk;
- private File mTestApkTwoCerts;
- private File mTestApkSourceStamp;
-
- // under test
- private AppIntegrityManagerServiceImpl mService;
-
- @Before
- public void setup() throws Exception {
- mTestApk = File.createTempFile("AppIntegrity", ".apk");
- try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_PATH)) {
- Files.copy(inputStream, mTestApk.toPath(), REPLACE_EXISTING);
- }
-
- mTestApkTwoCerts = File.createTempFile("AppIntegrityTwoCerts", ".apk");
- try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
- Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
- }
-
- mTestApkSourceStamp = File.createTempFile("AppIntegritySourceStamp", ".apk");
- try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_SOURCE_STAMP_PATH)) {
- Files.copy(inputStream, mTestApkSourceStamp.toPath(), REPLACE_EXISTING);
- }
-
- mService =
- new AppIntegrityManagerServiceImpl(
- mMockContext,
- mPackageManagerInternal,
- mHandler);
-
- mSpyPackageManager = spy(mRealContext.getPackageManager());
- // setup mocks to prevent NPE
- when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
- when(mMockContext.getResources()).thenReturn(mMockResources);
- when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
- // These are needed to override the Settings.Global.get result.
- when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
- setIntegrityCheckIncludesRuleProvider(true);
- }
-
- @After
- public void tearDown() throws Exception {
- mTestApk.delete();
- mTestApkTwoCerts.delete();
- mTestApkSourceStamp.delete();
- }
-
- @Test
- public void broadcastReceiverRegistration() throws Exception {
- allowlistUsAsRuleProvider();
- makeUsSystemApp();
- ArgumentCaptor<IntentFilter> intentFilterCaptor =
- ArgumentCaptor.forClass(IntentFilter.class);
-
- verify(mMockContext).registerReceiver(any(), intentFilterCaptor.capture(), any(), any());
- assertEquals(1, intentFilterCaptor.getValue().countActions());
- assertEquals(
- Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION,
- intentFilterCaptor.getValue().getAction(0));
- assertEquals(1, intentFilterCaptor.getValue().countDataTypes());
- assertEquals(PACKAGE_MIME_TYPE, intentFilterCaptor.getValue().getDataType(0));
- }
-
- @Test
- public void handleBroadcast_allow() throws Exception {
- allowlistUsAsRuleProvider();
- makeUsSystemApp();
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext)
- .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- Intent intent = makeVerificationIntent();
-
- broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
- runJobInHandler();
-
- verify(mPackageManagerInternal)
- .setIntegrityVerificationResult(
- 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- }
-
- private void allowlistUsAsRuleProvider() {
- Resources mockResources = mock(Resources.class);
- when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
- .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
- when(mMockContext.getResources()).thenReturn(mockResources);
- }
-
- private void runJobInHandler() {
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- // sendMessageAtTime is the first non-final method in the call chain when "post" is invoked.
- verify(mHandler).sendMessageAtTime(messageCaptor.capture(), anyLong());
- messageCaptor.getValue().getCallback().run();
- }
-
- private void makeUsSystemApp() throws Exception {
- makeUsSystemApp(true);
- }
-
- private void makeUsSystemApp(boolean isSystemApp) throws Exception {
- PackageInfo packageInfo =
- mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, 0);
- if (isSystemApp) {
- packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- } else {
- packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
- }
- doReturn(packageInfo)
- .when(mSpyPackageManager)
- .getPackageInfo(eq(TEST_FRAMEWORK_PACKAGE), anyInt());
- when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
- }
-
- private Intent makeVerificationIntent() throws Exception {
- PackageInfo packageInfo =
- mRealContext
- .getPackageManager()
- .getPackageInfo(
- TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNING_CERTIFICATES);
- doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
- doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
- doReturn(new String[]{INSTALLER}).when(mSpyPackageManager).getPackagesForUid(anyInt());
- return makeVerificationIntent(INSTALLER);
- }
-
- private Intent makeVerificationIntent(String installer) throws Exception {
- Intent intent = new Intent();
- intent.setDataAndType(Uri.fromFile(mTestApk), PACKAGE_MIME_TYPE);
- intent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
- intent.putExtra(EXTRA_VERIFICATION_ID, 1);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, PACKAGE_NAME);
- intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, installer);
- intent.putExtra(
- EXTRA_VERIFICATION_INSTALLER_UID,
- mMockContext.getPackageManager().getPackageUid(installer, /* flags= */ 0));
- intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, VERSION_CODE);
- return intent;
- }
-
- private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception {
- int value = shouldInclude ? 1 : 0;
- Settings.Global.putInt(
- mRealContext.getContentResolver(),
- Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- value);
- assertThat(
- Settings.Global.getInt(
- mRealContext.getContentResolver(),
- Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- -1)
- == 1)
- .isEqualTo(shouldInclude);
- }
-}