summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PREUPLOAD.cfg2
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java28
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java7
-rw-r--r--core/api/current.txt6
-rw-r--r--core/java/android/app/Activity.java15
-rw-r--r--core/java/android/app/ActivityThread.java65
-rw-r--r--core/java/android/app/Notification.java44
-rw-r--r--core/java/android/app/usage/IStorageStatsManager.aidl1
-rw-r--r--core/java/android/app/usage/StorageStats.java64
-rw-r--r--core/java/android/app/usage/flags.aconfig10
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java139
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java18
-rw-r--r--core/java/android/companion/AssociationRequest.java8
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig2
-rw-r--r--core/java/android/content/pm/multiuser.aconfig10
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java4
-rw-r--r--core/java/android/os/BatteryStats.java103
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionManager.java2
-rw-r--r--core/java/android/security/flags.aconfig8
-rw-r--r--core/java/android/text/Layout.java6
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java5
-rw-r--r--core/java/android/widget/RemoteViews.java58
-rw-r--r--core/java/android/window/DesktopModeFlags.java2
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig11
-rw-r--r--core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java8
-rw-r--r--core/java/com/android/internal/protolog/ProtoLog.java21
-rw-r--r--core/java/com/android/internal/protolog/common/LogLevel.java3
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/values/public-staging.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java61
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java104
-rw-r--r--core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt138
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java102
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/ProtoLogTest.java53
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/ProtoLogTestGroup.java85
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--data/keyboards/Android.bp11
-rw-r--r--data/keyboards/keyboards.mk10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt115
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java16
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt442
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt165
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt98
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt96
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt37
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt3
-rw-r--r--location/java/android/location/flags/location.aconfig10
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl2
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml13
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java6
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java12
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_filled.xml21
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_outline.xml21
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml35
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml13
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java39
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java15
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig17
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt23
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt74
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt77
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java54
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java35
-rw-r--r--packages/SystemUI/res/drawable/notification_menu_button_background.xml21
-rw-r--r--packages/SystemUI/res/drawable/unpin_icon.xml12
-rw-r--r--packages/SystemUI/res/layout/battery_percentage_view.xml2
-rw-r--r--packages/SystemUI/res/layout/keyguard_status_bar.xml2
-rw-r--r--packages/SystemUI/res/layout/promoted_menu_item.xml54
-rw-r--r--packages/SystemUI/res/layout/promoted_permission_guts.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml2
-rw-r--r--packages/SystemUI/res/layout/system_icons.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/strings.xml7
-rw-r--r--packages/SystemUI/res/values/styles.xml17
-rw-r--r--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OWNERS4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java12
-rw-r--r--ravenwood/api-maintainers.md66
-rw-r--r--ravenwood/test-authors.md90
-rw-r--r--ravenwood/texts/ravenwood-annotation-allowed-classes.txt4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java31
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java27
-rw-r--r--services/companion/java/com/android/server/companion/utils/MetricUtils.java8
-rw-r--r--services/companion/java/com/android/server/companion/utils/PermissionsUtils.java6
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java2
-rw-r--r--services/core/java/com/android/server/SystemConfig.java14
-rw-r--r--services/core/java/com/android/server/TradeInModeService.java29
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java8
-rw-r--r--services/core/java/com/android/server/am/BroadcastHistory.java11
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java29
-rw-r--r--services/core/java/com/android/server/health/HealthServiceWrapper.java67
-rw-r--r--services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java81
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java5
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java65
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java3
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java48
-rw-r--r--services/core/java/com/android/server/updates/CertPinInstallReceiver.java20
-rw-r--r--services/core/java/com/android/server/wm/TrustedPresentationListenerController.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/CreateRequestSession.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java7
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java12
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java15
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt176
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt48
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt7
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt605
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt301
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java64
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java7
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java254
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java52
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java73
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt101
-rw-r--r--tests/Input/src/android/hardware/input/InputManagerTest.kt15
-rw-r--r--tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt126
-rw-r--r--tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt66
-rw-r--r--tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt67
-rw-r--r--tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt5
-rw-r--r--tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt62
-rw-r--r--tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt138
-rw-r--r--tests/Input/src/com/android/server/input/BatteryControllerTests.kt321
-rw-r--r--tests/Input/src/com/android/server/input/InputDataStoreTests.kt504
-rw-r--r--tests/Input/src/com/android/server/input/InputGestureManagerTests.kt150
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt296
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt694
-rw-r--r--tests/Input/src/com/android/server/input/KeyRemapperTests.kt76
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt237
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt50
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt603
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt148
-rw-r--r--tests/Input/src/com/android/server/input/PointerIconCacheTest.kt20
-rw-r--r--tests/Input/src/com/android/test/input/InputEventAssignerTest.kt70
-rw-r--r--tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt27
-rw-r--r--tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt31
-rw-r--r--tests/Input/src/com/android/test/input/MockInputManagerRule.kt4
-rw-r--r--tests/Input/src/com/android/test/input/MotionPredictorTest.kt72
-rw-r--r--tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt15
-rw-r--r--tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt86
-rw-r--r--tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt6
-rw-r--r--tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt81
-rw-r--r--tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt14
-rw-r--r--tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt7
194 files changed, 6147 insertions, 3976 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e862cd9e0a95..843fde7baf2b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -19,7 +19,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
tests/
tools/
bpfmt = -d
-ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode,apct-tests
+ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode,libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode,apct-tests,tests/Input
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index f6ae56f01758..5b3b876edd3a 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -32,11 +32,13 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -153,6 +155,26 @@ public class BlobStoreManager {
private final Context mContext;
private final IBlobStoreManager mService;
+ // TODO: b/404309424 - Make these constants available using a test-api to avoid hardcoding
+ // them in tests.
+ /**
+ * The maximum allowed length for the package name, provided using
+ * {@link BlobStoreManager.Session#allowPackageAccess(String, byte[])}.
+ *
+ * This is the same limit that is already used for limiting the length of the package names
+ * at android.content.pm.parsing.FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE.
+ *
+ * @hide
+ */
+ public static final int MAX_PACKAGE_NAME_LENGTH = 223;
+ /**
+ * The maximum allowed length for the certificate, provided using
+ * {@link BlobStoreManager.Session#allowPackageAccess(String, byte[])}.
+ *
+ * @hide
+ */
+ public static final int MAX_CERTIFICATE_LENGTH = 32;
+
/** @hide */
public BlobStoreManager(@NonNull Context context, @NonNull IBlobStoreManager service) {
mContext = context;
@@ -786,6 +808,12 @@ public class BlobStoreManager {
*/
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
throws IOException {
+ Objects.requireNonNull(packageName);
+ Preconditions.checkArgument(packageName.length() <= MAX_PACKAGE_NAME_LENGTH,
+ "packageName is longer than " + MAX_PACKAGE_NAME_LENGTH + " chars");
+ Objects.requireNonNull(certificate);
+ Preconditions.checkArgument(certificate.length <= MAX_CERTIFICATE_LENGTH,
+ "certificate is longer than " + MAX_CERTIFICATE_LENGTH + " chars");
try {
mSession.allowPackageAccess(packageName, certificate);
} catch (ParcelableException e) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index ede29ec168c0..790d4e934317 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -16,6 +16,8 @@
package com.android.server.blob;
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
+import static android.app.blob.BlobStoreManager.MAX_CERTIFICATE_LENGTH;
+import static android.app.blob.BlobStoreManager.MAX_PACKAGE_NAME_LENGTH;
import static android.app.blob.XmlTags.ATTR_CREATION_TIME_MS;
import static android.app.blob.XmlTags.ATTR_ID;
import static android.app.blob.XmlTags.ATTR_PACKAGE;
@@ -328,6 +330,11 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
@NonNull byte[] certificate) {
assertCallerIsOwner();
Objects.requireNonNull(packageName, "packageName must not be null");
+ Preconditions.checkArgument(packageName.length() <= MAX_PACKAGE_NAME_LENGTH,
+ "packageName is longer than " + MAX_PACKAGE_NAME_LENGTH + " chars");
+ Objects.requireNonNull(certificate, "certificate must not be null");
+ Preconditions.checkArgument(certificate.length <= MAX_CERTIFICATE_LENGTH,
+ "certificate is longer than " + MAX_CERTIFICATE_LENGTH + " chars");
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
throw new IllegalStateException("Not allowed to change access type in state: "
diff --git a/core/api/current.txt b/core/api/current.txt
index bba21f418e41..151a6738505c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -286,7 +286,7 @@ package android {
field public static final String REQUEST_COMPANION_PROFILE_COMPUTER = "android.permission.REQUEST_COMPANION_PROFILE_COMPUTER";
field public static final String REQUEST_COMPANION_PROFILE_GLASSES = "android.permission.REQUEST_COMPANION_PROFILE_GLASSES";
field public static final String REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING";
- field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") public static final String REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING";
+ field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") public static final String REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE = "android.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE";
field public static final String REQUEST_COMPANION_PROFILE_WATCH = "android.permission.REQUEST_COMPANION_PROFILE_WATCH";
field public static final String REQUEST_COMPANION_RUN_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND";
field public static final String REQUEST_COMPANION_SELF_MANAGED = "android.permission.REQUEST_COMPANION_SELF_MANAGED";
@@ -2483,7 +2483,6 @@ package android {
field public static final int primary = 16908300; // 0x102000c
field public static final int progress = 16908301; // 0x102000d
field public static final int redo = 16908339; // 0x1020033
- field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final int remoteViewsMetricsId;
field public static final int replaceText = 16908340; // 0x1020034
field public static final int secondaryProgress = 16908303; // 0x102000f
field public static final int selectAll = 16908319; // 0x102001f
@@ -10086,7 +10085,7 @@ package android.companion {
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER) public static final String DEVICE_PROFILE_COMPUTER = "android.app.role.COMPANION_DEVICE_COMPUTER";
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES) public static final String DEVICE_PROFILE_GLASSES = "android.app.role.COMPANION_DEVICE_GLASSES";
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING) public static final String DEVICE_PROFILE_NEARBY_DEVICE_STREAMING = "android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING";
- field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING) public static final String DEVICE_PROFILE_SENSOR_DEVICE_STREAMING = "android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING";
+ field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE) public static final String DEVICE_PROFILE_VIRTUAL_DEVICE = "android.app.role.COMPANION_DEVICE_VIRTUAL_DEVICE";
field public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
}
@@ -61755,6 +61754,7 @@ package android.widget {
method public void setTextViewText(@IdRes int, CharSequence);
method public void setTextViewTextSize(@IdRes int, int, float);
method public void setUri(@IdRes int, String, android.net.Uri);
+ method @FlaggedApi("android.appwidget.flags.engagement_metrics") public void setUsageEventTag(@IdRes int, int);
method public void setViewLayoutHeight(@IdRes int, float, int);
method public void setViewLayoutHeightAttr(@IdRes int, @AttrRes int);
method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d5df48a2ea22..c129fde3f819 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3174,6 +3174,15 @@ public class Activity extends ContextThemeWrapper
throw new IllegalArgumentException("Expected non-null picture-in-picture params");
}
if (!mCanEnterPictureInPicture) {
+ if (isTvImplicitEnterPipProhibited()) {
+ // Don't throw exception on TV so that apps don't crash when not adapted to new
+ // restrictions.
+ Log.e(TAG,
+ "Activity must be resumed to enter picture-in-picture and not about to be"
+ + " paused. Implicit app entry is only permitted on TV if android"
+ + ".permission.TV_IMPLICIT_ENTER_PIP is held by the app.");
+ return false;
+ }
throw new IllegalStateException("Activity must be resumed to enter"
+ " picture-in-picture");
}
@@ -3212,7 +3221,7 @@ public class Activity extends ContextThemeWrapper
return ActivityTaskManager.getMaxNumPictureInPictureActions(this);
}
- private boolean isImplicitEnterPipProhibited() {
+ private boolean isTvImplicitEnterPipProhibited() {
PackageManager pm = getPackageManager();
if (android.app.Flags.enableTvImplicitEnterPipRestriction()) {
return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
@@ -9346,7 +9355,7 @@ public class Activity extends ContextThemeWrapper
+ mComponent.getClassName());
}
- if (isImplicitEnterPipProhibited()) {
+ if (isTvImplicitEnterPipProhibited()) {
mCanEnterPictureInPicture = false;
}
@@ -9376,7 +9385,7 @@ public class Activity extends ContextThemeWrapper
final void performUserLeaving() {
onUserInteraction();
- if (isImplicitEnterPipProhibited()) {
+ if (isTvImplicitEnterPipProhibited()) {
mCanEnterPictureInPicture = false;
}
onUserLeaveHint();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f44c2305591d..4987624be719 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -40,6 +40,7 @@ import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
import android.annotation.NonNull;
@@ -116,6 +117,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
import android.graphics.Typeface;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.media.MediaFrameworkInitializer;
import android.media.MediaFrameworkPlatformInitializer;
@@ -863,7 +865,8 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
- static final class ReceiverData extends BroadcastReceiver.PendingResult {
+ @VisibleForTesting(visibility = PACKAGE)
+ public static final class ReceiverData extends BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
boolean ordered, boolean sticky, boolean assumeDelivered, IBinder token,
int sendingUser, int sendingUid, String sendingPackage) {
@@ -871,6 +874,11 @@ public final class ActivityThread extends ClientTransactionHandler
assumeDelivered, token, sendingUser, intent.getFlags(), sendingUid,
sendingPackage);
this.intent = intent;
+ if (com.android.window.flags.Flags.supportWidgetIntentsOnConnectedDisplay()) {
+ mOptions = ActivityOptions.fromBundle(resultExtras);
+ } else {
+ mOptions = null;
+ }
}
@UnsupportedAppUsage
@@ -879,12 +887,16 @@ public final class ActivityThread extends ClientTransactionHandler
ActivityInfo info;
@UnsupportedAppUsage
CompatibilityInfo compatInfo;
+ @Nullable
+ final ActivityOptions mOptions;
+
public String toString() {
return "ReceiverData{intent=" + intent + " packageName=" +
info.packageName + " resultCode=" + getResultCode()
+ " resultData=" + getResultData() + " resultExtras="
+ getResultExtras(false) + " sentFromUid="
- + getSentFromUid() + " sentFromPackage=" + getSentFromPackage() + "}";
+ + getSentFromUid() + " sentFromPackage=" + getSentFromPackage()
+ + " mOptions=" + mOptions + "}";
}
}
@@ -4985,6 +4997,7 @@ public final class ActivityThread extends ClientTransactionHandler
final String attributionTag = data.info.attributionTags[0];
context = (ContextImpl) context.createAttributionContext(attributionTag);
}
+ context = (ContextImpl) createDisplayContextIfNeeded(context, data);
java.lang.ClassLoader cl = context.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess(
@@ -5033,6 +5046,54 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ /**
+ * Creates a display context if the broadcast was initiated with a launch display ID.
+ *
+ * <p>When a broadcast initiates from a widget on a secondary display, the originating
+ * display ID is included as an extra in the intent. This is accomplished by
+ * {@link PendingIntentRecord#createSafeActivityOptionsBundle}, which transfers the launch
+ * display ID from ActivityOptions into the intent's extras bundle. This method checks for
+ * the presence of that extra and creates a display context associated with the initiated
+ * display if it exists. This ensures that when the {@link BroadcastReceiver} invokes
+ * {@link Context#startActivity(Intent)}, the activity is launched on the correct display.
+ *
+ * @param context The original context of the receiver.
+ * @param data The {@link ReceiverData} containing optional display information.
+ * @return A display context if applicable; otherwise the original context.
+ */
+ @NonNull
+ @VisibleForTesting(visibility = PRIVATE)
+ public Context createDisplayContextIfNeeded(@NonNull Context context,
+ @NonNull ReceiverData data) {
+ if (!com.android.window.flags.Flags.supportWidgetIntentsOnConnectedDisplay()) {
+ return context;
+ }
+
+ final ActivityOptions options = data.mOptions;
+ if (options == null) {
+ return context;
+ }
+
+ final int launchDisplayId = options.getLaunchDisplayId();
+ if (launchDisplayId == INVALID_DISPLAY) {
+ return context;
+ }
+
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ if (dm == null) {
+ return context;
+ }
+
+ final Display display = dm.getDisplay(launchDisplayId);
+ if (display == null) {
+ Slog.w(TAG, "Unable to create a display context for nonexistent display "
+ + launchDisplayId);
+ return context;
+ }
+
+ return context.createDisplayContext(display);
+ }
+
// Instantiate a BackupAgent and tell it that it's alive
private void handleCreateBackupAgent(CreateBackupAgentData data) {
if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f5277fd86a57..4c3deada4d76 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3253,9 +3253,24 @@ public class Notification implements Parcelable
* @hide
*/
public boolean hasTitle() {
- return extras != null
- && (!TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE))
- || !TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE_BIG)));
+ if (extras == null) {
+ return false;
+ }
+ // CallStyle notifications only use the other person's name as the title.
+ if (isStyle(CallStyle.class)) {
+ Person person = extras.getParcelable(EXTRA_CALL_PERSON, Person.class);
+ return person != null && !TextUtils.isEmpty(person.getName());
+ }
+ // non-CallStyle notifications can use EXTRA_TITLE
+ if (!TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE))) {
+ return true;
+ }
+ // BigTextStyle notifications first use EXTRA_TITLE_BIG
+ if (isStyle(BigTextStyle.class)) {
+ return !TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE_BIG));
+ } else {
+ return false;
+ }
}
/**
@@ -3280,12 +3295,23 @@ public class Notification implements Parcelable
*/
@FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
public boolean hasPromotableCharacteristics() {
- return isColorizedRequested()
- && isOngoingEvent()
- && hasTitle()
- && !isGroupSummary()
- && !containsCustomViews()
- && hasPromotableStyle();
+ if (!isOngoingEvent() || isGroupSummary() || containsCustomViews() || !hasTitle()) {
+ return false;
+ }
+ // Only "Ongoing CallStyle" notifications are promotable without EXTRA_COLORIZED
+ if (isOngoingCallStyle()) {
+ return true;
+ }
+ return isColorizedRequested() && hasPromotableStyle();
+ }
+
+ /** Returns whether the notification is CallStyle.forOngoingCall(). */
+ private boolean isOngoingCallStyle() {
+ if (!isStyle(CallStyle.class)) {
+ return false;
+ }
+ int callType = extras.getInt(EXTRA_CALL_TYPE, CallStyle.CALL_TYPE_UNKNOWN);
+ return callType == CallStyle.CALL_TYPE_ONGOING;
}
/**
diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl
index b5036da33a95..390300360c8f 100644
--- a/core/java/android/app/usage/IStorageStatsManager.aidl
+++ b/core/java/android/app/usage/IStorageStatsManager.aidl
@@ -30,6 +30,7 @@ interface IStorageStatsManager {
long getCacheBytes(String volumeUuid, String callingPackage);
long getCacheQuotaBytes(String volumeUuid, int uid, String callingPackage);
StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId, String callingPackage);
+ StorageStats queryArtManagedStats(String packageName, int userId, int uid);
StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage);
StorageStats queryStatsForUser(String volumeUuid, int userId, String callingPackage);
ExternalStorageStats queryExternalStatsForUser(String volumeUuid, int userId, String callingPackage);
diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java
index 7bfaef4e9857..d01b423d67f2 100644
--- a/core/java/android/app/usage/StorageStats.java
+++ b/core/java/android/app/usage/StorageStats.java
@@ -22,7 +22,10 @@ import android.annotation.IntDef;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -34,6 +37,9 @@ import java.lang.annotation.RetentionPolicy;
* @see StorageStatsManager
*/
public final class StorageStats implements Parcelable {
+ /** @hide */ public String packageName;
+ /** @hide */ public int userHandle;
+ /** @hide */ public int uid;
/** @hide */ public long codeBytes;
/** @hide */ public long dataBytes;
/** @hide */ public long cacheBytes;
@@ -130,6 +136,14 @@ public final class StorageStats implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface AppDataType {}
+ private static final String TAG = "StorageStats";
+
+ /**
+ * artStatsFetched is only applicable when
+ * Flags.getAppArtManagedBytes() is true;
+ */
+ private boolean artStatsFetched;
+
/**
* Return the size of app. This includes {@code APK} files, optimized
* compiler output, and unpacked native libraries.
@@ -157,9 +171,9 @@ public final class StorageStats implements Parcelable {
@FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
public long getAppBytesByDataType(@AppDataType int dataType) {
switch (dataType) {
- case APP_DATA_TYPE_FILE_TYPE_DEXOPT_ARTIFACT: return dexoptBytes;
- case APP_DATA_TYPE_FILE_TYPE_REFERENCE_PROFILE: return refProfBytes;
- case APP_DATA_TYPE_FILE_TYPE_CURRENT_PROFILE: return curProfBytes;
+ case APP_DATA_TYPE_FILE_TYPE_DEXOPT_ARTIFACT: return getDexoptBytes();
+ case APP_DATA_TYPE_FILE_TYPE_REFERENCE_PROFILE: return getRefProfBytes();
+ case APP_DATA_TYPE_FILE_TYPE_CURRENT_PROFILE: return getCurProfBytes();
case APP_DATA_TYPE_FILE_TYPE_APK: return apkBytes;
case APP_DATA_TYPE_LIB: return libBytes;
case APP_DATA_TYPE_FILE_TYPE_DM: return dmBytes;
@@ -215,6 +229,9 @@ public final class StorageStats implements Parcelable {
/** {@hide} */
public StorageStats(Parcel in) {
+ this.packageName = in.readString8();
+ this.userHandle = in.readInt();
+ this.uid = in.readInt();
this.codeBytes = in.readLong();
this.dataBytes = in.readLong();
this.cacheBytes = in.readLong();
@@ -234,6 +251,9 @@ public final class StorageStats implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString8(packageName);
+ dest.writeInt(userHandle);
+ dest.writeInt(uid);
dest.writeLong(codeBytes);
dest.writeLong(dataBytes);
dest.writeLong(cacheBytes);
@@ -257,4 +277,42 @@ public final class StorageStats implements Parcelable {
return new StorageStats[size];
}
};
+
+ private void getArtManagedStats() {
+ try {
+ IStorageStatsManager storageStatsManagerService;
+ // Fetch art stats only if it is not already fetched.
+ if (Flags.getAppArtManagedBytes() && !artStatsFetched) {
+ android.os.IBinder binder = ServiceManager.getService("storagestats");
+ storageStatsManagerService = IStorageStatsManager.Stub.asInterface(binder);
+
+ StorageStats newStats =
+ storageStatsManagerService.queryArtManagedStats(packageName, userHandle, uid);
+
+ dexoptBytes = newStats.dexoptBytes;
+ curProfBytes = newStats.curProfBytes;
+ refProfBytes = newStats.refProfBytes;
+
+ artStatsFetched = true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get art stats", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ private long getDexoptBytes() {
+ getArtManagedStats();
+ return dexoptBytes;
+ }
+
+ private long getCurProfBytes() {
+ getArtManagedStats();
+ return curProfBytes;
+ }
+
+ private long getRefProfBytes() {
+ getArtManagedStats();
+ return refProfBytes;
+ }
}
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index 04c36867271c..520284993841 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -49,6 +49,16 @@ flag {
}
flag {
+ name: "get_app_art_managed_bytes"
+ namespace: "system_performance"
+ description: "Bug fixing flag for optional collection of app ART managed file stats"
+ bug: "395548922"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "disable_idle_check"
namespace: "backstage_power"
description: "disable idle check for USER_SYSTEM during boot up"
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index b9b5c6a8bbc3..33326347fda0 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -16,10 +16,15 @@
package android.appwidget;
+import static android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS;
+import static android.appwidget.flags.Flags.engagementMetrics;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -38,6 +43,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcelable;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
@@ -48,6 +54,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.AbsListView;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
@@ -57,8 +64,11 @@ import android.widget.RemoteViews.InteractionHandler;
import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -99,7 +109,8 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
int mViewMode = VIEW_MODE_NOINIT;
// If true, we should not try to re-apply the RemoteViews on the next inflation.
boolean mColorMappingChanged = false;
- private InteractionHandler mInteractionHandler;
+ @NonNull
+ private InteractionLogger mInteractionLogger = new InteractionLogger();
private boolean mOnLightBackground;
private SizeF mCurrentSize = null;
private RemoteViews.ColorResources mColorResources = null;
@@ -124,7 +135,7 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
*/
public AppWidgetHostView(Context context, InteractionHandler handler) {
this(context, android.R.anim.fade_in, android.R.anim.fade_out);
- mInteractionHandler = getHandler(handler);
+ setInteractionHandler(handler);
}
/**
@@ -145,13 +156,29 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
/**
* Pass the given handler to RemoteViews when updating this widget. Unless this
- * is done immediatly after construction, a call to {@link #updateAppWidget(RemoteViews)}
+ * is done immediately after construction, a call to {@link #updateAppWidget(RemoteViews)}
* should be made.
*
* @hide
*/
public void setInteractionHandler(InteractionHandler handler) {
- mInteractionHandler = getHandler(handler);
+ if (handler instanceof InteractionLogger logger) {
+ // Nested AppWidgetHostViews should reuse the parent logger instead of wrapping it.
+ mInteractionLogger = logger;
+ } else {
+ mInteractionLogger = new InteractionLogger(handler);
+ }
+ }
+
+ /**
+ * Return the InteractionLogger used by this class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public InteractionLogger getInteractionLogger() {
+ return mInteractionLogger;
}
/**
@@ -588,7 +615,7 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
if (!mColorMappingChanged && rvToApply.canRecycleView(mView)) {
try {
- rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
+ rvToApply.reapply(mContext, mView, mInteractionLogger, mCurrentSize,
mColorResources);
content = mView;
mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
@@ -602,7 +629,7 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
// Try normal RemoteView inflation
if (content == null) {
try {
- content = rvToApply.apply(mContext, this, mInteractionHandler,
+ content = rvToApply.apply(mContext, this, mInteractionLogger,
mCurrentSize, mColorResources);
mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
if (LOGD) Log.d(TAG, "had to inflate new layout");
@@ -660,7 +687,7 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
mView,
mAsyncExecutor,
new ViewApplyListener(remoteViews, layoutId, true),
- mInteractionHandler,
+ mInteractionLogger,
mCurrentSize,
mColorResources);
} catch (Exception e) {
@@ -672,7 +699,7 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
this,
mAsyncExecutor,
new ViewApplyListener(remoteViews, layoutId, false),
- mInteractionHandler,
+ mInteractionLogger,
mCurrentSize,
mColorResources);
}
@@ -711,7 +738,7 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
AppWidgetHostView.this,
mAsyncExecutor,
new ViewApplyListener(mViews, mLayoutId, false),
- mInteractionHandler,
+ mInteractionLogger,
mCurrentSize);
} else {
applyContent(null, false, e);
@@ -916,21 +943,6 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
return null;
}
- private InteractionHandler getHandler(InteractionHandler handler) {
- return (view, pendingIntent, response) -> {
- AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
- if (manager != null) {
- manager.noteAppWidgetTapped(mAppWidgetId);
- }
- if (handler != null) {
- return handler.onInteraction(view, pendingIntent, response);
- } else {
- return RemoteViews.startPendingIntent(view, pendingIntent,
- response.getLaunchOptions(view));
- }
- };
- }
-
/**
* Set the dynamically overloaded color resources.
*
@@ -1016,4 +1028,83 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
post(this::handleViewError);
}
}
+
+ /**
+ * This class is used to track user interactions with this widget.
+ * @hide
+ */
+ public class InteractionLogger implements RemoteViews.InteractionHandler {
+ // Max number of clicked and scrolled IDs stored per impression.
+ public static final int MAX_NUM_ITEMS = 10;
+ // Clicked views
+ @NonNull
+ private final Set<Integer> mClickedIds = new ArraySet<>(MAX_NUM_ITEMS);
+ // Scrolled views
+ @NonNull
+ private final Set<Integer> mScrolledIds = new ArraySet<>(MAX_NUM_ITEMS);
+ @Nullable
+ private RemoteViews.InteractionHandler mInteractionHandler = null;
+
+ InteractionLogger() {
+ }
+
+ InteractionLogger(@Nullable InteractionHandler handler) {
+ mInteractionHandler = handler;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ public Set<Integer> getClickedIds() {
+ return mClickedIds;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ public Set<Integer> getScrolledIds() {
+ return mScrolledIds;
+ }
+
+ @Override
+ public boolean onInteraction(View view, PendingIntent pendingIntent,
+ RemoteViews.RemoteResponse response) {
+ if (engagementMetrics() && mClickedIds.size() < MAX_NUM_ITEMS) {
+ mClickedIds.add(getMetricsId(view));
+ }
+ AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
+ if (manager != null) {
+ manager.noteAppWidgetTapped(mAppWidgetId);
+ }
+
+ if (mInteractionHandler != null) {
+ return mInteractionHandler.onInteraction(view, pendingIntent, response);
+ } else {
+ return RemoteViews.startPendingIntent(view, pendingIntent,
+ response.getLaunchOptions(view));
+ }
+ }
+
+ @Override
+ public void onScroll(@NonNull AbsListView view) {
+ if (!engagementMetrics()) return;
+
+ if (mScrolledIds.size() < MAX_NUM_ITEMS) {
+ mScrolledIds.add(getMetricsId(view));
+ }
+
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onScroll(view);
+ }
+ }
+
+ @FlaggedApi(FLAG_ENGAGEMENT_METRICS)
+ private int getMetricsId(@NonNull View view) {
+ int viewId = view.getId();
+ Object metricsTag = view.getTag(com.android.internal.R.id.remoteViewsMetricsId);
+ if (metricsTag instanceof Integer tag) {
+ viewId = tag;
+ }
+ return viewId;
+ }
+ }
}
+
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index b54e17beb100..52315d68afda 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -515,12 +515,13 @@ public class AppWidgetManager {
/**
* This bundle extra describes which views have been clicked during a single impression of the
- * widget. It is an integer array of view IDs of the clicked views.
+ * widget. It is an integer array of view IDs of the clicked views. The array may contain up to
+ * 10 distinct IDs per event.
*
- * Widget providers may set a different ID for event purposes by setting the
- * {@link android.R.id.remoteViewsMetricsId} int tag on the view.
+ * Widget providers may set a different ID for event logging by setting the usage event tag on
+ * the view with {@link RemoteViews#setUsageEventTag}.
*
- * @see android.views.RemoteViews.setIntTag
+ * @see android.widget.RemoteViews#setUsageEventTag
*/
@FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
public static final String EXTRA_EVENT_CLICKED_VIEWS =
@@ -528,12 +529,13 @@ public class AppWidgetManager {
/**
* This bundle extra describes which views have been scrolled during a single impression of the
- * widget. It is an integer array of view IDs of the scrolled views.
+ * widget. It is an integer array of view IDs of the scrolled views. The array may contain up to
+ * 10 distinct IDs per event.
*
- * Widget providers may set a different ID for event purposes by setting the
- * {@link android.R.id.remoteViewsMetricsId} int tag on the view.
+ * Widget providers may set a different ID for event logging by setting the usage event tag on
+ * the view with {@link RemoteViews#setUsageEventTag}.
*
- * @see android.views.RemoteViews.setIntTag
+ * @see android.widget.RemoteViews#setUsageEventTag
*/
@FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
public static final String EXTRA_EVENT_SCROLLED_VIEWS =
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 11e20e65d355..9641d7e69d4a 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -140,15 +140,15 @@ public final class AssociationRequest implements Parcelable {
* IMU between an Android host and a nearby device.
* <p>
* Only applications that have been granted
- * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING}
+ * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE}
* are allowed to request to be associated with such devices.
*
* @see AssociationRequest.Builder#setDeviceProfile
*/
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
- @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING)
- public static final String DEVICE_PROFILE_SENSOR_DEVICE_STREAMING =
- "android.app.role.COMPANION_DEVICE_SENSOR_DEVICE_STREAMING";
+ @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE)
+ public static final String DEVICE_PROFILE_VIRTUAL_DEVICE =
+ "android.app.role.COMPANION_DEVICE_VIRTUAL_DEVICE";
/**
* Device profile: Android Automotive Projection
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 161f05bc5139..c29f1528be89 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -154,7 +154,7 @@ flag {
flag {
namespace: "virtual_devices"
name: "viewconfiguration_apis"
- description: "APIs for settings ViewConfiguration attributes on virtual devices"
+ description: "APIs for setting ViewConfiguration attributes on virtual devices"
bug: "370720522"
is_exported: true
}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3dbd5b239ae5..fefa8ab8f000 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -384,6 +384,16 @@ flag {
is_fixed_read_only: true
}
+flag {
+ name: "require_pin_before_user_deletion"
+ namespace: "multiuser"
+ description: "Require credential authentication when a user tries to delete themselves or another user"
+ bug: "342395399"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
# This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
flag {
name: "enable_private_space_features"
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index bebca57125b6..42df43e4d436 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -475,6 +475,7 @@ public final class DisplayManagerGlobal {
synchronized (mLock) {
if (!mShouldImplicitlyRegisterRrChanges) {
mShouldImplicitlyRegisterRrChanges = true;
+ Slog.i(TAG, "Implicitly registering for refresh rate");
updateCallbackIfNeededLocked();
}
}
@@ -1759,6 +1760,9 @@ public final class DisplayManagerGlobal {
synchronized (mLock) {
mDispatchNativeCallbacks = true;
if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ if (!mShouldImplicitlyRegisterRrChanges) {
+ Slog.i(TAG, "Choreographer implicitly registered for the refresh rate.");
+ }
mShouldImplicitlyRegisterRrChanges = true;
}
registerCallbackIfNeededLocked();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc1dee7a5747..b2f25212729e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -6948,8 +6948,9 @@ public abstract class BatteryStats {
// This constant MUST be incremented whenever the history dump format changes.
private static final int FORMAT_VERSION = 2;
- private final SimpleDateFormat mHistoryItemTimestampFormat =
- new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
+ private final boolean mPerformanceBaseline;
+ private final HistoryLogTimeFormatter mHistoryLogTimeFormatter;
+ private final SimpleDateFormat mHistoryItemTimestampFormat;
private final SimpleDateFormat mCurrentTimeEventTimeFormat =
new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
@@ -6960,6 +6961,7 @@ public abstract class BatteryStats {
private final int mFormatVersion;
+ private final StringBuilder mStringBuilder = new StringBuilder();
int oldState = 0;
int oldState2 = 0;
int oldLevel = -1;
@@ -6974,18 +6976,30 @@ public abstract class BatteryStats {
long lastTime = -1;
public HistoryPrinter() {
- this(TimeZone.getDefault());
+ this(0);
}
- public HistoryPrinter(TimeZone timeZone) {
+ public HistoryPrinter(int flags) {
+ this(TimeZone.getDefault(), flags);
+ }
+
+ public HistoryPrinter(TimeZone timeZone, int flags) {
this(com.android.server.power.optimization.Flags
.extendedBatteryHistoryContinuousCollectionEnabled()
- ? FORMAT_VERSION : FORMAT_LEGACY, timeZone);
+ ? FORMAT_VERSION : FORMAT_LEGACY, timeZone, flags);
}
- private HistoryPrinter(int formatVersion, TimeZone timeZone) {
+ private HistoryPrinter(int formatVersion, TimeZone timeZone, int flags) {
mFormatVersion = formatVersion;
- mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone);
+ mPerformanceBaseline = (flags & DUMP_DEBUG_PERF_BASELINE) != 0;
+ if (mPerformanceBaseline) {
+ mHistoryItemTimestampFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
+ mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone);
+ mHistoryLogTimeFormatter = null;
+ } else {
+ mHistoryItemTimestampFormat = null;
+ mHistoryLogTimeFormatter = new HistoryLogTimeFormatter(timeZone);
+ }
mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone);
}
@@ -7019,7 +7033,8 @@ public abstract class BatteryStats {
@SuppressWarnings("JavaUtilDate")
private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
- StringBuilder item = new StringBuilder();
+ StringBuilder item = mStringBuilder;
+ item.setLength(0);
if (!checkin) {
item.append(" ");
if (mFormatVersion == FORMAT_LEGACY) {
@@ -7029,8 +7044,13 @@ public abstract class BatteryStats {
item.append(rec.numReadInts);
item.append(") ");
} else {
- mDate.setTime(rec.currentTime);
- item.append(mHistoryItemTimestampFormat.format(mDate)).append(' ');
+ if (mPerformanceBaseline) {
+ mDate.setTime(rec.currentTime);
+ item.append(mHistoryItemTimestampFormat.format(mDate)).append(' ');
+ } else {
+ mHistoryLogTimeFormatter.append(item, rec.currentTime);
+ item.append(' ');
+ }
}
} else {
item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
@@ -7399,6 +7419,64 @@ public abstract class BatteryStats {
sb.append(":");
sb.append(stime);
}
+
+ /**
+ * In essence, this is a wrapper over SimpleDateFormat that takes advantage
+ * of the fact that events in battery history are closely spaced, which allows it
+ * to reuse the results of the most expensive formatting work.
+ */
+ private static class HistoryLogTimeFormatter {
+ private static final long HOUR_MILLIS = 3600000L;
+ private static final long MINUTE_MILLIS = 60000L;
+ private final SimpleDateFormat mDateFormat =
+ new SimpleDateFormat("MM-dd HH:", Locale.US);
+ private final Date mDate = new Date();
+
+ private final long mTimeZoneOffset;
+ private long mCachedHour;
+ private String mCachedHourFormatted;
+
+ private HistoryLogTimeFormatter(TimeZone timeZone) {
+ mTimeZoneOffset = timeZone.getRawOffset();
+ mDateFormat.getCalendar().setTimeZone(timeZone);
+ }
+
+ /* Appends timestampMs formatted as "MM-dd HH:mm:ss.SSS" */
+ void append(StringBuilder sb, long timestampMs) {
+ long localTime = timestampMs + mTimeZoneOffset;
+ long hour = localTime / HOUR_MILLIS;
+ if (hour != mCachedHour) {
+ mDate.setTime(timestampMs);
+ mCachedHourFormatted = mDateFormat.format(mDate);
+ mCachedHour = hour;
+ }
+ sb.append(mCachedHourFormatted);
+
+ long remainder = localTime % HOUR_MILLIS;
+
+ long minutes = remainder / MINUTE_MILLIS;
+ if (minutes < 10) {
+ sb.append('0');
+ }
+ sb.append(minutes).append(':');
+
+ remainder = remainder % MINUTE_MILLIS;
+ long seconds = remainder / 1000;
+ if (seconds < 10) {
+ sb.append('0');
+ }
+ sb.append(seconds).append('.');
+
+ long millis = remainder % 1000;
+ if (millis < 100) {
+ sb.append('0');
+ if (millis < 10) {
+ sb.append('0');
+ }
+ }
+ sb.append(millis);
+ }
+ }
}
private void printSizeValue(PrintWriter pw, long size) {
@@ -7584,9 +7662,10 @@ public abstract class BatteryStats {
public static final int DUMP_INCLUDE_HISTORY = 1<<4;
public static final int DUMP_VERBOSE = 1<<5;
public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6;
+ public static final int DUMP_DEBUG_PERF_BASELINE = 1 << 7;
private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) {
- final HistoryPrinter hprinter = new HistoryPrinter();
+ final HistoryPrinter hprinter = new HistoryPrinter(flags);
synchronized (this) {
if (!checkin) {
final long historyTotalSize = getHistoryTotalSize();
@@ -8522,7 +8601,7 @@ public abstract class BatteryStats {
}
// History data (HISTORY_DATA)
- final HistoryPrinter hprinter = new HistoryPrinter();
+ final HistoryPrinter hprinter = new HistoryPrinter(flags);
long lastTime = -1;
long baseTime = -1;
boolean printed = false;
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index cb2b13d1e120..ecc5fd468ee8 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -308,7 +308,7 @@ public final class AdvancedProtectionManager {
}
/**
- * Enables or disables advanced protection on the device.
+ * Enables or disables advanced protection on the device. Can only be called by an admin user.
*
* @param enabled {@code true} to enable advanced protection, {@code false} to disable it.
* @hide
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 0a922d61a786..09137c3a7b65 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -52,14 +52,6 @@ flag {
}
flag {
- name: "extend_vb_chain_to_updated_apk"
- namespace: "hardware_backed_security"
- description: "Use v4 signature and fs-verity to chain verification of allowlisted APKs to Verified Boot"
- bug: "277916185"
- is_fixed_read_only: true
-}
-
-flag {
name: "binary_transparency_sepolicy_hash"
namespace: "hardware_backed_security"
description: "Collect sepolicy hash from sysfs"
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 0152c52a6753..ebd6efac3d96 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1074,15 +1074,15 @@ public abstract class Layout {
public void onCharacterBounds(int index, int lineNum, float left, float top,
float right, float bottom) {
- var newBackground = determineContrastingBackgroundColor(index);
- var hasBgColorChanged = newBackground != bgPaint.getColor();
-
// Skip processing if the character is a space or a tap to avoid
// rendering an abrupt, empty rectangle.
if (TextLine.isLineEndSpace(mText.charAt(index))) {
return;
}
+ var newBackground = determineContrastingBackgroundColor(index);
+ var hasBgColorChanged = newBackground != bgPaint.getColor();
+
// To avoid highlighting emoji sequences, we use Extended_Pictgraphs as a
// heuristic. Highlighting is skipped based on code points, not glyph type
// (text vs. color), so emojis with default text presentation are
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 52102714eb5f..ac9ebe58e73e 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -88,7 +88,7 @@ public class ApkSignatureSchemeV4Verifier {
if (signatureBytes != null && signatureBytes.length > 0) {
needsConsistencyCheck = false;
signature = V4Signature.readFrom(signatureBytes);
- } else if (android.security.Flags.extendVbChainToUpdatedApk()) {
+ } else {
// 2. Try fs-verity next. fs-verity checks against the Merkle tree, but the
// v4 signature file (including a raw root hash) is managed separately. We need to
// ensure the signed data from the file is consistent with the actual file.
@@ -101,9 +101,6 @@ public class ApkSignatureSchemeV4Verifier {
throw new SignatureNotFoundException(
"Failed to obtain signature bytes from .idsig");
}
- } else {
- throw new SignatureNotFoundException(
- "Failed to obtain signature bytes from IncFS.");
}
if (!signature.isVersionSupported()) {
throw new SecurityException(
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3dfbc2517986..1e0c4906e6ed 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -548,6 +548,25 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Set a view tag associating a View with an ID to be used for widget interaction usage events
+ * ({@link android.app.usage.UsageEvents.Event}). When this RemoteViews is applied to a bound
+ * widget, any clicks or scrolls on the tagged view will be reported to
+ * {@link android.app.usage.UsageStatsManager} using this tag.
+ *
+ * @param viewId ID of the View whose tag will be set
+ * @param tag The integer tag to use for the event
+ *
+ * @see android.appwidget.AppWidgetManager#EVENT_TYPE_WIDGET_INTERACTION
+ * @see android.appwidget.AppWidgetManager#EXTRA_EVENT_CLICKED_VIEWS
+ * @see android.appwidget.AppWidgetManager#EXTRA_EVENT_SCROLLED_VIEWS
+ * @see android.app.usage.UsageStatsManager#queryEventsForSelf
+ */
+ @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
+ public void setUsageEventTag(@IdRes int viewId, int tag) {
+ addAction(new SetIntTagAction(viewId, com.android.internal.R.id.remoteViewsMetricsId, tag));
+ }
+
+ /**
* Set that it is disallowed to reapply another remoteview with the same layout as this view.
* This should be done if an action is destroying the view tree of the base layout.
*
@@ -666,6 +685,14 @@ public class RemoteViews implements Parcelable, Filter {
View view,
PendingIntent pendingIntent,
RemoteResponse response);
+
+ /**
+ * Invoked when an AbsListView is scrolled.
+ * @param view view that was scrolled
+ *
+ * @hide
+ */
+ default void onScroll(@NonNull AbsListView view) {}
}
/**
@@ -1313,6 +1340,21 @@ public class RemoteViews implements Parcelable, Filter {
// a type error.
throw new ActionException(throwable);
}
+ if (adapterView instanceof AbsListView listView) {
+ listView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState != SCROLL_STATE_IDLE) {
+ params.handler.onScroll(view);
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+ }
+ });
+ }
}
@Override
@@ -1804,6 +1846,19 @@ public class RemoteViews implements Parcelable, Filter {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(mIntent, mIsAsync);
v.setRemoteViewsInteractionHandler(params.handler);
+ v.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState != SCROLL_STATE_IDLE) {
+ params.handler.onScroll(view);
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+ }
+ });
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
v.setRemoteViewsAdapter(mIntent, mIsAsync);
@@ -1894,7 +1949,8 @@ public class RemoteViews implements Parcelable, Filter {
target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
return;
}
- target.setOnClickListener(v -> mResponse.handleViewInteraction(v, params.handler));
+ target.setOnClickListener(v ->
+ mResponse.handleViewInteraction(v, params.handler));
}
@Override
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 28a922d56019..fdaa41c63343 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -150,7 +150,7 @@ public enum DesktopModeFlags {
INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES(
Flags::inheritTaskBoundsForTrampolineTaskLaunches, true),
SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX(
- Flags::skipDecorViewRelayoutWhenClosingBugfix, false),
+ Flags::skipDecorViewRelayoutWhenClosingBugfix, true),
// go/keep-sorted end
;
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 2fd295e5dfc2..e2eb193293c9 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -152,3 +152,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "support_widget_intents_on_connected_display"
+ description: "Launch widget intents on originating display"
+ bug: "358368849"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java b/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
index 34e04181388d..256a3ffd6456 100644
--- a/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
@@ -19,6 +19,7 @@ package com.android.internal.protolog;
import static com.android.internal.protolog.ProtoLog.REQUIRE_PROTOLOGTOOL;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.text.TextUtils;
import android.util.Log;
@@ -31,14 +32,15 @@ import java.util.Collections;
import java.util.List;
/**
- * Class only create and used to server temporarily for when there is source code pre-processing by
+ * Class only created and used to serve temporarily for when there is source code pre-processing by
* the ProtoLog tool, when the tracing to Perfetto flag is off, and the static REQUIRE_PROTOLOGTOOL
* boolean is false. In which case we simply want to log protolog message to logcat. Note, that this
* means that in such cases there is no real advantage of using protolog over logcat.
*
- * @deprecated Should not be used. This is just a temporary class to support a legacy behavior.
+ * NOTE: Should not be used in real products as this mostly removes the benefits of protolog. This
+ * is just a temporary class to support a legacy behavior and tests running on the host-side.
*/
-@Deprecated
+@RavenwoodKeepWholeClass
public class LogcatOnlyProtoLogImpl implements IProtoLog {
private static final String LOG_TAG = LogcatOnlyProtoLogImpl.class.getName();
diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java
index c81af959f36c..915b981a7e7a 100644
--- a/core/java/com/android/internal/protolog/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/ProtoLog.java
@@ -17,6 +17,8 @@
package com.android.internal.protolog;
import android.os.ServiceManager;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
@@ -47,6 +49,7 @@ import java.util.HashSet;
* Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool
* during build.
*/
+@RavenwoodKeepWholeClass
// LINT.IfChange
public class ProtoLog {
// LINT.ThenChange(frameworks/base/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt)
@@ -73,13 +76,25 @@ public class ProtoLog {
// These tracing instances are only used when we cannot or do not preprocess the source
// files to extract out the log strings. Otherwise, the trace calls are replaced with calls
// directly to the generated tracing implementations.
- if (android.tracing.Flags.perfettoProtologTracing()) {
- initializePerfettoProtoLog(groups);
- } else {
+ if (logOnlyToLogcat()) {
sProtoLogInstance = new LogcatOnlyProtoLogImpl();
+ } else {
+ initializePerfettoProtoLog(groups);
}
}
+ @RavenwoodReplace(reason = "Always use the Log backend on ravenwood, not Perfetto")
+ private static boolean logOnlyToLogcat() {
+ return !android.tracing.Flags.perfettoProtologTracing();
+ }
+
+ private static boolean logOnlyToLogcat$ravenwood() {
+ // We don't want to initialize Perfetto data sources and have to deal with Perfetto
+ // when running tests on the host side, instead just log everything to logcat which has
+ // already been made compatible with ravenwood.
+ return true;
+ }
+
private static void initializePerfettoProtoLog(IProtoLogGroup... groups) {
var datasource = getSharedSingleInstanceDataSource();
diff --git a/core/java/com/android/internal/protolog/common/LogLevel.java b/core/java/com/android/internal/protolog/common/LogLevel.java
index b5541ae81c2d..ea62a665c980 100644
--- a/core/java/com/android/internal/protolog/common/LogLevel.java
+++ b/core/java/com/android/internal/protolog/common/LogLevel.java
@@ -16,6 +16,9 @@
package com.android.internal.protolog.common;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
+@RavenwoodKeepWholeClass
public enum LogLevel {
DEBUG("d", 1),
VERBOSE("v", 2),
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9e0200481421..f62ce278f28a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4554,12 +4554,12 @@
android:protectionLevel="signature|privileged" />
<!-- Allows application to request to stream content from an Android host to a nearby device
- ({@link android.companion.AssociationRequest#DEVICE_PROFILE_SENSOR_DEVICE_STREAMING})
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_VIRTUAL_DEVICE})
by {@link android.companion.CompanionDeviceManager}.
<p>Not for use by third-party applications.
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
-->
- <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING"
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE"
android:protectionLevel="signature|privileged"
android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role" />
@@ -9287,11 +9287,15 @@
<receiver android:name="com.android.server.updates.CertPinInstallReceiver"
android:exported="true"
+ android:systemUserOnly="true"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_PINS" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
</receiver>
<receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver"
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index ac1e841d3143..ed524054a5d4 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -128,7 +128,7 @@
<staging-public-group type="id" first-id="0x01b20000">
<!-- @FlaggedApi(android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS) -->
- <public name="remoteViewsMetricsId"/>
+ <public name="removed_remoteViewsMetricsId"/>
<!-- @FlaggedApi("android.view.accessibility.a11y_selection_api") -->
<public name="accessibilityActionSetExtendedSelection"/>
</staging-public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 219ac3f89997..b9ca96f31cd4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5224,6 +5224,7 @@
<java-symbol type="id" name="remote_views_next_child" />
<java-symbol type="id" name="remote_views_stable_id" />
<java-symbol type="id" name="remote_views_override_id" />
+ <java-symbol type="id" name="remoteViewsMetricsId" />
<!-- View and control prompt -->
<java-symbol type="drawable" name="ic_accessibility_24dp" />
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 157c74abc5de..0287956bd07f 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -462,7 +462,7 @@ public class NotificationTest {
@Test
@EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
- public void testHasPromotableCharacteristics() {
+ public void testHasPromotableCharacteristics_bigText_bigTitle() {
Notification n = new Notification.Builder(mContext, "test")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
@@ -475,6 +475,20 @@ public class NotificationTest {
@Test
@EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+ public void testHasPromotableCharacteristics_bigText_normalTitle() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.BigTextStyle())
+ .setContentTitle("TITLE")
+ .setColor(Color.WHITE)
+ .setColorized(true)
+ .setOngoing(true)
+ .build();
+ assertThat(n.hasPromotableCharacteristics()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
public void testHasPromotableCharacteristics_notOngoing() {
Notification n = new Notification.Builder(mContext, "test")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -526,6 +540,51 @@ public class NotificationTest {
@Test
@EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+ public void testHasPromotableCharacteristics_noStyle_onlyBigTitle() {
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_TITLE_BIG, "BIG");
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setColor(Color.WHITE)
+ .setColorized(true)
+ .setOngoing(true)
+ .addExtras(extras)
+ .build();
+ assertThat(n.hasPromotableCharacteristics()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+ public void testHasPromotableCharacteristics_ongoingCallStyle_notColorized() {
+ PendingIntent intent = PendingIntent.getActivity(
+ mContext, 0, new Intent("test1"), PendingIntent.FLAG_IMMUTABLE);
+ Person person = new Person.Builder().setName("Caller").build();
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(Notification.CallStyle.forOngoingCall(person, intent))
+ .setColor(Color.WHITE)
+ .setOngoing(true)
+ .build();
+ assertThat(n.hasPromotableCharacteristics()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+ public void testHasPromotableCharacteristics_incomingCallStyle_notColorized() {
+ PendingIntent intent = PendingIntent.getActivity(
+ mContext, 0, new Intent("test1"), PendingIntent.FLAG_IMMUTABLE);
+ Person person = new Person.Builder().setName("Caller").build();
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(Notification.CallStyle.forIncomingCall(person, intent, intent))
+ .setColor(Color.WHITE)
+ .setOngoing(true)
+ .build();
+ assertThat(n.hasPromotableCharacteristics()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
public void testHasPromotableCharacteristics_groupSummary() {
Notification n = new Notification.Builder(mContext, "test")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 1f1000f2800d..74023670c460 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -25,6 +25,8 @@ import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.window.flags.Flags.FLAG_SUPPORT_WIDGET_INTENTS_ON_CONNECTED_DISPLAY;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -39,11 +41,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ActivityThread.ReceiverData;
import android.app.Application;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
@@ -158,10 +164,7 @@ public class ActivityThreadTest {
@After
public void tearDown() {
- if (mCreatedVirtualDisplays != null) {
- mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
- mCreatedVirtualDisplays = null;
- }
+ tearDownVirtualDisplays();
WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
ClientTransactionListenerController.getInstance()
.unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
@@ -1007,6 +1010,92 @@ public class ActivityThreadTest {
.that(systemContext.getApplicationInfo()).isSameInstanceAs(newAppInfo);
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_SUPPORT_WIDGET_INTENTS_ON_CONNECTED_DISPLAY)
+ public void tesScheduleReceiver_withLaunchDisplayId_receivesDisplayContext() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final Display virtualDisplay = createVirtualDisplay(context, 100 /* w */, 100 /* h */);
+ final int virtualDisplayId = virtualDisplay.getDisplayId();
+ final ActivityOptions activityOptions =
+ ActivityOptions.makeBasic().setLaunchDisplayId(virtualDisplayId);
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+
+ final ReceiverData data = createReceiverData(activityOptions.toBundle());
+ final Context resultContext =
+ activityThread.createDisplayContextIfNeeded(context, data);
+
+ final Display resultDisplay = resultContext.getDisplayNoVerify();
+ assertThat(resultDisplay).isNotNull();
+ assertThat(resultDisplay.getDisplayId()).isEqualTo(virtualDisplayId);
+ assertThat(resultContext.getAssociatedDisplayId()).isEqualTo(virtualDisplayId);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SUPPORT_WIDGET_INTENTS_ON_CONNECTED_DISPLAY)
+ public void tesScheduleReceiver_withNotExistDisplayId_receivesNoneUiContext() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final Display virtualDisplay = createVirtualDisplay(context, 100 /* w */, 100 /* h */);
+ final int virtualDisplayId = virtualDisplay.getDisplayId();
+ final ActivityOptions activityOptions =
+ ActivityOptions.makeBasic().setLaunchDisplayId(virtualDisplayId);
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ tearDownVirtualDisplays();
+
+ final ReceiverData data = createReceiverData(activityOptions.toBundle());
+ final Context resultContext = activityThread.createDisplayContextIfNeeded(context, data);
+
+ assertThat(resultContext).isEqualTo(context);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SUPPORT_WIDGET_INTENTS_ON_CONNECTED_DISPLAY)
+ public void tesScheduleReceiver_withInvalidDisplay_receivesNoneUiContext() {
+ final Context context = mock(Context.class);
+ final ActivityOptions activityOptions =
+ ActivityOptions.makeBasic().setLaunchDisplayId(INVALID_DISPLAY);
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+
+ final ReceiverData data = createReceiverData(activityOptions.toBundle());
+ final Context resultContext = activityThread.createDisplayContextIfNeeded(context, data);
+
+ verify(context, never()).createDisplayContext(any());
+ assertThat(resultContext).isEqualTo(context);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SUPPORT_WIDGET_INTENTS_ON_CONNECTED_DISPLAY)
+ public void tesScheduleReceiver_withoutDisplayManagerService_receivesNoneUiContext() {
+ final Context context = mock(Context.class);
+ when(context.getSystemService(DisplayManager.class)).thenReturn(null);
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+
+ final ReceiverData data = createReceiverData(null /* resultExtras */);
+ final Context resultContext = activityThread.createDisplayContextIfNeeded(context, data);
+
+ verify(context, never()).createDisplayContext(any());
+ assertThat(resultContext).isEqualTo(context);
+ }
+
+ @Test
+ public void tesScheduleReceiver_withoutActivityOptions_receivesNoneUiContext() {
+ final Context context = mock(Context.class);
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+
+ final ReceiverData data = createReceiverData(null /* resultExtras */);
+ final Context resultContext = activityThread.createDisplayContextIfNeeded(context, data);
+
+ verify(context, never()).createDisplayContext(any());
+ assertThat(resultContext).isEqualTo(context);
+ }
+
+ @NonNull
+ private ReceiverData createReceiverData(@Nullable Bundle resultExtras) {
+ return new ReceiverData(new Intent("test.action.WIDGET_ITEM_CLICK"),
+ 0 /* resultCode */, null /* resultData */, resultExtras, false /* ordered */,
+ false /* sticky */, false /* assumeDelivered */, null /* token */,
+ 0 /* sendingUser */, -1 /* sendingUid */, null /* sendingPackage */);
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int, ActivityWindowInfo)} to try to push activity configuration to the
@@ -1056,6 +1145,13 @@ public class ActivityThreadTest {
return virtualDisplay.getDisplay();
}
+ private void tearDownVirtualDisplays() {
+ if (mCreatedVirtualDisplays != null) {
+ mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
+ mCreatedVirtualDisplays = null;
+ }
+ }
+
private static ActivityClientRecord getActivityClientRecord(Activity activity) {
final ActivityThread thread = activity.getActivityThread();
final IBinder token = activity.getActivityToken();
diff --git a/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt b/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt
index ea1158c88055..0135378ba681 100644
--- a/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt
+++ b/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt
@@ -16,14 +16,33 @@
package android.appwidget
+import android.app.PendingIntent
+import android.appwidget.AppWidgetHostView.InteractionLogger.MAX_NUM_ITEMS
+import android.content.Intent
import android.graphics.Rect
+import android.view.View
+import android.widget.ListView
+import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.frameworks.coretests.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AppWidgetEventsTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext!!
+ private val hostView = AppWidgetHostView(context).apply {
+ setAppWidget(0, AppWidgetManager.getInstance(context).installedProviders.first())
+ }
+ private val pendingIntent = PendingIntent.getActivity(
+ context,
+ 0,
+ Intent(),
+ PendingIntent.FLAG_IMMUTABLE,
+ )
+
@Test
fun createWidgetInteractionEvent() {
val appWidgetId = 1
@@ -48,4 +67,123 @@ class AppWidgetEventsTest {
assertThat(bundle.getIntArray(AppWidgetManager.EXTRA_EVENT_SCROLLED_VIEWS))
.asList().containsExactly(scrolled[0], scrolled[1], scrolled[2])
}
+
+ @Test
+ fun interactionLogger_click() {
+ val itemCount = MAX_NUM_ITEMS + 1
+ // Set a different value for the viewId to test that the logger always uses the
+ // metrics tag if available.
+ fun viewId(i: Int) = i + Int.MIN_VALUE
+ val remoteViews = RemoteViews(context.packageName, R.layout.remote_views_test).apply {
+ for (i in 0 until itemCount) {
+ val metricsTag = i
+ val item =
+ RemoteViews(context.packageName, R.layout.remote_views_text, viewId(i)).apply {
+ setUsageEventTag(viewId(i), metricsTag)
+ setOnClickPendingIntent(viewId(i), pendingIntent)
+ }
+ addView(R.id.layout, item)
+ }
+ }
+ hostView.updateAppWidget(remoteViews)
+ assertThat(hostView.interactionLogger.clickedIds).isEmpty()
+
+
+ for (i in 0 until itemCount.minus(1)) {
+ val item = hostView.findViewById<View>(viewId(i))
+ assertThat(item).isNotNull()
+ assertThat(item.performClick()).isTrue()
+ assertThat(hostView.interactionLogger.clickedIds)
+ .containsExactlyElementsIn(0..i)
+ }
+ assertThat(hostView.interactionLogger.clickedIds).hasSize(MAX_NUM_ITEMS)
+
+ // Last item click should not be recorded because we've reached MAX_VIEW_IDS
+ val lastItem = hostView.findViewById<View>(viewId(itemCount - 1))
+ assertThat(lastItem).isNotNull()
+ assertThat(lastItem.performClick()).isTrue()
+ assertThat(hostView.interactionLogger.clickedIds).hasSize(MAX_NUM_ITEMS)
+ assertThat(hostView.interactionLogger.clickedIds)
+ .containsExactlyElementsIn(0..itemCount.minus(2))
+ }
+
+ @Test
+ fun interactionLogger_click_listItem() {
+ val itemCount = 5
+ val remoteViews = RemoteViews(context.packageName, R.layout.remote_views_list).apply {
+ setPendingIntentTemplate(R.id.list, pendingIntent)
+ setRemoteAdapter(
+ R.id.list,
+ RemoteViews.RemoteCollectionItems.Builder().run {
+ for (i in 0 until itemCount) {
+ val item = RemoteViews(context.packageName, R.layout.remote_views_test)
+ item.setOnClickFillInIntent(R.id.text, Intent())
+ item.setUsageEventTag(R.id.text, i)
+ addItem(i.toLong(), item)
+ }
+ build()
+ }
+ )
+ setUsageEventTag(R.id.list, -1)
+ }
+ hostView.updateAppWidget(remoteViews)
+ assertThat(hostView.interactionLogger.clickedIds).isEmpty()
+
+ val list = hostView.findViewById<ListView>(R.id.list)
+ assertThat(list).isNotNull()
+ list.layout(0, 0, 500, 500)
+ for (i in 0 until itemCount) {
+ val item = list.getChildAt(i).findViewById<View>(R.id.text)
+ assertThat(item.performClick()).isTrue()
+ assertThat(hostView.interactionLogger.clickedIds)
+ .containsExactlyElementsIn(0..i)
+ }
+ }
+
+ @Test
+ fun interactionLogger_scroll() {
+ val itemCount = MAX_NUM_ITEMS + 1
+ // Set a different value for the viewId to test that the logger always uses the
+ // metrics tag if available.
+ fun viewId(i: Int) = i + Int.MIN_VALUE
+ val remoteViews = RemoteViews(context.packageName, R.layout.remote_views_test).apply {
+ for (i in 0 until itemCount) {
+ val metricsTag = i
+ val item =
+ RemoteViews(context.packageName, R.layout.remote_views_list, viewId(i)).apply {
+ setUsageEventTag(viewId(i), metricsTag)
+ setRemoteAdapter(
+ viewId(i),
+ RemoteViews.RemoteCollectionItems.Builder().run {
+ addItem(
+ 0L,
+ RemoteViews(context.packageName, R.layout.remote_views_test)
+ )
+ build()
+ }
+ )
+ }
+ addView(R.id.layout, item)
+ }
+ }
+ hostView.updateAppWidget(remoteViews)
+ assertThat(hostView.interactionLogger.scrolledIds).isEmpty()
+
+ for (i in 0 until itemCount.minus(1)) {
+ val item = hostView.findViewById<ListView>(viewId(i))
+ assertThat(item).isNotNull()
+ item.fling(/* velocityY= */ 100)
+ assertThat(hostView.interactionLogger.scrolledIds)
+ .containsExactlyElementsIn(0..i)
+ }
+ assertThat(hostView.interactionLogger.scrolledIds).hasSize(MAX_NUM_ITEMS)
+
+ // Last item scroll should not be recorded because we've reached MAX_VIEW_IDS
+ val lastItem = hostView.findViewById<ListView>(viewId(itemCount - 1))
+ assertThat(lastItem).isNotNull()
+ lastItem.fling(/* velocityY= */ 100)
+ assertThat(hostView.interactionLogger.scrolledIds).hasSize(MAX_NUM_ITEMS)
+ assertThat(hostView.interactionLogger.scrolledIds)
+ .containsExactlyElementsIn(0..itemCount.minus(2))
+ }
}
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 11ec9f8e1912..7d8afcabad7b 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -1029,51 +1029,16 @@ public class LayoutTest {
@Test
@RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
- public void highContrastTextEnabled_testWhitespaceText_DrawsBackgroundsWithAdjacentLetters() {
- mTextPaint.setColor(Color.BLACK);
- SpannableString spannedText = new SpannableString("Test\tTap and Space");
-
- // Set the entire text to white initially
- spannedText.setSpan(
- new ForegroundColorSpan(Color.WHITE),
- /* start= */ 0,
- /* end= */ spannedText.length(),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE
- );
-
- // Find the whitespace character and set its color to black
- for (int i = 0; i < spannedText.length(); i++) {
- if (Character.isWhitespace(spannedText.charAt(i))) {
- spannedText.setSpan(
- new ForegroundColorSpan(Color.BLACK),
- i,
- i + 1,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE
- );
- }
- }
-
- Layout layout = new StaticLayout(spannedText, mTextPaint, mWidth,
- mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
-
- MockCanvas c = new MockCanvas(/* width= */ 256, /* height= */ 256);
- c.setHighContrastTextEnabled(true);
- layout.draw(
- c,
- /* highlightPaths= */ null,
- /* highlightPaints= */ null,
- /* selectionPath= */ null,
- /* selectionPaint= */ null,
- /* cursorOffsetVertical= */ 0
- );
+ public void highContrastTextEnabled_testWhiteSpaceWithinText_drawsSameBackgroundswithText() {
+ SpannableString spannedText = new SpannableString("Hello\tWorld !");
+ testSpannableStringAppliesAllColorsCorrectly(spannedText);
+ }
- List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
- for (int i = 0; i < drawCommands.size(); i++) {
- MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
- if (drawCommand.rect != null) {
- expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK);
- }
- }
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testWhiteSpaceAtStart_drawsCorrectBackgroundsOnText() {
+ SpannableString spannedText = new SpannableString(" HelloWorld!");
+ testSpannableStringAppliesAllColorsCorrectly(spannedText);
}
@Test
@@ -1331,5 +1296,54 @@ public class LayoutTest {
"",
new boolean[]{false});
}
+
+ private void testSpannableStringAppliesAllColorsCorrectly(SpannableString spannedText) {
+ for (int textColor : new int[] {Color.WHITE, Color.BLACK}) {
+ final int contrastingColor = textColor == Color.WHITE ? Color.BLACK : Color.WHITE;
+ // Set the paint color to the contrasting color to verify the high contrast text
+ // background rect color is correct.
+ mTextPaint.setColor(contrastingColor);
+
+ // Set the entire text to test color initially
+ spannedText.setSpan(
+ new ForegroundColorSpan(textColor),
+ /* start= */ 0,
+ /* end= */ spannedText.length(),
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+ );
+
+ Layout layout = new StaticLayout(spannedText, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+ MockCanvas c = new MockCanvas(/* width= */ 256, /* height= */ 256);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(
+ c,
+ /* highlightPaths= */ null,
+ /* highlightPaints= */ null,
+ /* selectionPath= */ null,
+ /* selectionPaint= */ null,
+ /* cursorOffsetVertical= */ 0
+ );
+
+ int numBackgroundsFound = 0;
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.rect != null) {
+ numBackgroundsFound++;
+ // Verifies the background color of the high-contrast rectangle drawn behind
+ // the text. In high-contrast mode, the background color should contrast with
+ // the text color. 'contrastingColor' represents the expected background color,
+ // which is the inverse of the text color (e.g., if text is white, background
+ // is black, and vice versa).
+ expect.that(removeAlpha(drawCommand.paint.getColor()))
+ .isEqualTo(contrastingColor);
+ }
+ }
+ expect.that(numBackgroundsFound).isLessThan(spannedText.length());
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/logging/ProtoLogTest.java b/core/tests/coretests/src/com/android/internal/logging/ProtoLogTest.java
new file mode 100644
index 000000000000..90b6902629e6
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/ProtoLogTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.logging;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ProtoLogTest {
+ @Test
+ public void canTrace() {
+ ProtoLog.init(TEST_GROUP_1, TEST_GROUP_2);
+
+ ProtoLog.v(TEST_GROUP_1, "Verbose message");
+ ProtoLog.d(TEST_GROUP_1, "Debug message");
+ ProtoLog.i(TEST_GROUP_1, "Info message");
+ ProtoLog.w(TEST_GROUP_1, "Warning message");
+ ProtoLog.e(TEST_GROUP_1, "Error message");
+ ProtoLog.wtf(TEST_GROUP_1, "Wtf message");
+
+ ProtoLog.v(TEST_GROUP_2, "Verbose message");
+ ProtoLog.d(TEST_GROUP_2, "Debug message");
+ ProtoLog.i(TEST_GROUP_2, "Info message");
+ ProtoLog.w(TEST_GROUP_2, "Warning message");
+ ProtoLog.e(TEST_GROUP_2, "Error message");
+ ProtoLog.wtf(TEST_GROUP_2, "Wtf message");
+ }
+
+ private static final IProtoLogGroup TEST_GROUP_1 = new ProtoLogTestGroup("TEST_TAG_1", 1);
+ private static final IProtoLogGroup TEST_GROUP_2 = new ProtoLogTestGroup("TEST_TAG_2", 2);
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/ProtoLogTestGroup.java b/core/tests/coretests/src/com/android/internal/logging/ProtoLogTestGroup.java
new file mode 100644
index 000000000000..ecfaae5ea0c5
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/ProtoLogTestGroup.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.logging;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+class ProtoLogTestGroup implements IProtoLogGroup {
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+ private final int mId;
+
+ ProtoLogTestGroup(String tag, int id) {
+ this(true, true, false, tag, id);
+ }
+
+ ProtoLogTestGroup(
+ boolean enabled, boolean logToProto, boolean logToLogcat, String tag, int id) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ this.mId = id;
+ }
+
+ @Override
+ public String name() {
+ return mTag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 1dd0465f691e..1d8eed2749d5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -424,7 +424,7 @@ applications that come with the platform
<permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING" />
- <permission name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING" />
+ <permission name="android.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER" />
<permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<permission name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" />
diff --git a/data/keyboards/Android.bp b/data/keyboards/Android.bp
index 423b55bd85db..69b29bd5c7d3 100644
--- a/data/keyboards/Android.bp
+++ b/data/keyboards/Android.bp
@@ -12,12 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
genrule {
name: "validate_framework_keymaps",
srcs: [
- "*.kl",
- "*.kcm",
"*.idc",
+ "*.kcm",
+ "*.kl",
],
tools: ["validatekeymaps"],
out: ["stamp"],
@@ -33,7 +37,6 @@ prebuilt_usr_keylayout {
srcs: [
"*.kl",
],
- no_full_install: true,
}
prebuilt_usr_keychars {
@@ -41,7 +44,6 @@ prebuilt_usr_keychars {
srcs: [
"*.kcm",
],
- no_full_install: true,
}
prebuilt_usr_idc {
@@ -49,5 +51,4 @@ prebuilt_usr_idc {
srcs: [
"*.idc",
],
- no_full_install: true,
}
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index c7ce8cd6693a..47bc63268754 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -14,9 +14,7 @@
# Warning: this is actually a product definition, to be inherited from
-PRODUCT_COPY_FILES := \
- $(call find-copy-subdir-files,*.kl,$(LOCAL_PATH),system/usr/keylayout) \
- $(call find-copy-subdir-files,*.kcm,$(LOCAL_PATH),system/usr/keychars) \
- $(call find-copy-subdir-files,*.idc,$(LOCAL_PATH),system/usr/idc)
-
-
+PRODUCT_PACKAGES += \
+ keylayout_data \
+ keychars_data \
+ idc_data
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
index 8e3dc4c36c1d..711667760314 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
@@ -34,6 +34,8 @@ import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.view.animation.TranslateAnimation;
+import com.android.wm.shell.shared.animation.Interpolators;
+
import java.util.function.Consumer;
/**
@@ -196,6 +198,8 @@ public class SizeChangeAnimation {
float startScaleY = scaleFactor * ((float) startBounds.height()) / endBounds.height()
+ (1.f - scaleFactor);
final AnimationSet animSet = new AnimationSet(true);
+ // Use a linear interpolator so the driving ValueAnimator sets the interpolation
+ animSet.setInterpolator(Interpolators.LINEAR);
final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
scaleAnim.setDuration(scalePeriod);
@@ -244,6 +248,8 @@ public class SizeChangeAnimation {
+ (1.f - scaleFactor));
AnimationSet snapAnimSet = new AnimationSet(true);
+ // Use a linear interpolator so the driving ValueAnimator sets the interpolation
+ snapAnimSet.setInterpolator(Interpolators.LINEAR);
// Animation for the "old-state" snapshot that is atop the task.
final Animation snapAlphaAnim = new AlphaAnimation(1.f, 0.f);
snapAlphaAnim.setDuration(scalePeriod);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 4900b6fc77ea..7ae9de8ee65d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -2959,6 +2959,9 @@ public class BubbleStackView extends FrameLayout
if (mIsExpanded) {
mExpandedViewAnimationController.animateForImeVisibilityChange(visible);
BubbleExpandedView expandedView = getExpandedView();
+ if (expandedView != null) {
+ expandedView.setImeVisible(visible);
+ }
if (mPositioner.showBubblesVertically() && expandedView != null) {
float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex,
getState()).y;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index b89bfd5c969e..ea365efcb400 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -615,7 +615,7 @@ public class BubbleBarAnimationHelper {
bbev.setSurfaceZOrderedOnTop(true);
a.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION);
- a.setInterpolator(Interpolators.EMPHASIZED);
+ a.setInterpolator(EMPHASIZED);
a.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 07745d487f64..46c9b07fb802 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -101,7 +101,6 @@ import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeMoveToDisplayTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
-import com.android.wm.shell.desktopmode.DesktopPipTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -793,7 +792,6 @@ public abstract class WMShellModule {
OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
DesksOrganizer desksOrganizer,
Optional<DesksTransitionObserver> desksTransitionObserver,
- Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
UserProfileContexts userProfileContexts,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DragToDisplayTransitionHandler dragToDisplayTransitionHandler,
@@ -837,7 +835,6 @@ public abstract class WMShellModule {
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionObserver.get(),
- desktopPipTransitionObserver,
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -1057,7 +1054,8 @@ public abstract class WMShellModule {
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
- Optional<CompatUIHandler> compatUI
+ Optional<CompatUIHandler> compatUI,
+ DesksOrganizer desksOrganizer
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -1075,7 +1073,8 @@ public abstract class WMShellModule {
activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger,
desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler,
desktopModeCompatPolicy, desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController, compatUI.orElse(null)));
+ multiDisplayDragMoveIndicatorController, compatUI.orElse(null),
+ desksOrganizer));
}
@WMSingleton
@@ -1250,7 +1249,6 @@ public abstract class WMShellModule {
Transitions transitions,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
- Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
Optional<BackAnimationController> backAnimationController,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
ShellInit shellInit) {
@@ -1263,7 +1261,6 @@ public abstract class WMShellModule {
transitions,
shellTaskOrganizer,
desktopMixedTransitionHandler.get(),
- desktopPipTransitionObserver,
backAnimationController.get(),
desktopWallpaperActivityTokenProvider,
shellInit)));
@@ -1285,19 +1282,6 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static Optional<DesktopPipTransitionObserver> provideDesktopPipTransitionObserver(
- Context context
- ) {
- if (DesktopModeStatus.canEnterDesktopMode(context)
- && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
- return Optional.of(
- new DesktopPipTransitionObserver());
- }
- return Optional.empty();
- }
-
- @WMSingleton
- @Provides
static Optional<DesktopMixedTransitionHandler> provideDesktopMixedTransitionHandler(
Context context,
Transitions transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 6f0919e1d045..c5f956a80702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.dagger.pip;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
+import android.window.DesktopModeFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -42,6 +43,8 @@ import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.desktopmode.DesktopPipTransitionController;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
@@ -55,6 +58,7 @@ import com.android.wm.shell.pip2.phone.PipTransition;
import com.android.wm.shell.pip2.phone.PipTransitionState;
import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -91,12 +95,13 @@ public abstract class Pip2Module {
DisplayController displayController,
Optional<SplitScreenController> splitScreenControllerOptional,
PipDesktopState pipDesktopState,
+ Optional<DesktopPipTransitionController> desktopPipTransitionController,
PipInteractionHandler pipInteractionHandler) {
return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
pipScheduler, pipStackListenerController, pipDisplayLayoutState,
pipUiStateChangeController, displayController, splitScreenControllerOptional,
- pipDesktopState, pipInteractionHandler);
+ pipDesktopState, desktopPipTransitionController, pipInteractionHandler);
}
@WMSingleton
@@ -250,6 +255,22 @@ public abstract class Pip2Module {
dragToDesktopTransitionHandlerOptional, rootTaskDisplayAreaOrganizer);
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopPipTransitionController> provideDesktopPipTransitionController(
+ Context context, Optional<DesktopTasksController> desktopTasksControllerOptional,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
+ PipDesktopState pipDesktopState
+ ) {
+ if (DesktopModeStatus.canEnterDesktopMode(context)
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
+ return Optional.of(
+ new DesktopPipTransitionController(desktopTasksControllerOptional.get(),
+ desktopUserRepositoriesOptional.get(), pipDesktopState));
+ }
+ return Optional.empty();
+ }
+
@BindsOptionalOf
abstract DragToDesktopTransitionHandler optionalDragToDesktopTransitionHandler();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index ea2fdc0ee8ed..0a3e2cc3b434 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -56,13 +56,6 @@ class DesktopDisplayModeController(
@ShellMainThread private val mainHandler: Handler,
) {
- private val onTabletModeChangedListener =
- object : InputManager.OnTabletModeChangedListener {
- override fun onTabletModeChanged(whenNanos: Long, inTabletMode: Boolean) {
- refreshDisplayWindowingMode()
- }
- }
-
private val inputDeviceListener =
object : InputManager.InputDeviceListener {
override fun onInputDeviceAdded(deviceId: Int) {
@@ -80,10 +73,6 @@ class DesktopDisplayModeController(
init {
if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- inputManager.registerOnTabletModeChangedListener(
- onTabletModeChangedListener,
- mainHandler,
- )
inputManager.registerInputDeviceListener(inputDeviceListener, mainHandler)
}
}
@@ -139,7 +128,7 @@ class DesktopDisplayModeController(
return true
}
if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- if (isInClamshellMode() || hasAnyMouseDevice()) {
+ if (hasAnyTouchpadDevice() && hasAnyPhysicalKeyboardDevice()) {
return true
}
}
@@ -186,17 +175,25 @@ class DesktopDisplayModeController(
private fun hasExternalDisplay() =
rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
- private fun hasAnyMouseDevice() =
- inputManager.inputDeviceIds.any {
- inputManager.getInputDevice(it)?.supportsSource(InputDevice.SOURCE_MOUSE) == true
+ private fun hasAnyTouchpadDevice() =
+ inputManager.inputDeviceIds.any { deviceId ->
+ inputManager.getInputDevice(deviceId)?.let { device ->
+ device.supportsSource(InputDevice.SOURCE_TOUCHPAD) && device.isEnabled()
+ } ?: false
}
- private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
+ private fun hasAnyPhysicalKeyboardDevice() =
+ inputManager.inputDeviceIds.any { deviceId ->
+ inputManager.getInputDevice(deviceId)?.let { device ->
+ !device.isVirtual() && device.isFullKeyboard() && device.isEnabled()
+ } ?: false
+ }
private fun isDefaultDisplayDesktopEligible(): Boolean {
- val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
- "Display object of DEFAULT_DISPLAY must be non-null."
- }
+ val display =
+ requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+ "Display object of DEFAULT_DISPLAY must be non-null."
+ }
return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt
new file mode 100644
index 000000000000..88468531cc47
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager
+import android.os.IBinder
+import android.window.DesktopExperienceFlags
+import android.window.WindowContainerTransaction
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.common.pip.PipDesktopState
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+
+/**
+ * Controller to perform extra handling to PiP transitions that are entering while in Desktop mode.
+ */
+class DesktopPipTransitionController(
+ private val desktopTasksController: DesktopTasksController,
+ private val desktopUserRepositories: DesktopUserRepositories,
+ private val pipDesktopState: PipDesktopState,
+) {
+
+ /**
+ * This is called by [PipTransition#handleRequest] when a request for entering PiP is received.
+ *
+ * @param wct WindowContainerTransaction that will apply these changes
+ * @param transition that will apply this transaction
+ * @param taskInfo of the task that is entering PiP
+ */
+ fun handlePipTransition(
+ wct: WindowContainerTransaction,
+ transition: IBinder,
+ taskInfo: ActivityManager.RunningTaskInfo,
+ ) {
+ if (!pipDesktopState.isDesktopWindowingPipEnabled()) {
+ return
+ }
+
+ // Early return if the transition is a synthetic transition that is not backed by a true
+ // system transition.
+ if (transition == DesktopTasksController.SYNTHETIC_TRANSITION) {
+ logD("handlePipTransitionIfInDesktop: SYNTHETIC_TRANSITION, not a true transition")
+ return
+ }
+
+ val taskId = taskInfo.taskId
+ val displayId = taskInfo.displayId
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isAnyDeskActive(displayId)) {
+ logD("handlePipTransitionIfInDesktop: PiP transition is not in Desktop session")
+ return
+ }
+
+ val deskId =
+ desktopRepository.getActiveDeskId(displayId)
+ ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ logW(
+ "handlePipTransitionIfInDesktop: " +
+ "Active desk not found for display id %d",
+ displayId,
+ )
+ return
+ } else {
+ checkNotNull(desktopRepository.getDefaultDeskId(displayId)) {
+ "$TAG: handlePipTransitionIfInDesktop: " +
+ "Expected a default desk to exist in display with id $displayId"
+ }
+ }
+
+ val isLastTask =
+ desktopRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = deskId,
+ displayId = displayId,
+ )
+ if (!isLastTask) {
+ logD("handlePipTransitionIfInDesktop: PiP task is not last visible task in Desk")
+ return
+ }
+
+ val desktopExitRunnable =
+ desktopTasksController.performDesktopExitCleanUp(
+ wct = wct,
+ deskId = deskId,
+ displayId = displayId,
+ willExitDesktop = true,
+ )
+ desktopExitRunnable?.invoke(transition)
+ }
+
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logD(msg: String, vararg arguments: Any?) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private companion object {
+ private const val TAG = "DesktopPipTransitionController"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
deleted file mode 100644
index efd3866e1bc4..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.desktopmode
-
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
-import android.os.IBinder
-import android.window.DesktopModeFlags
-import android.window.TransitionInfo
-import com.android.internal.protolog.ProtoLog
-import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
-
-/**
- * Observer of PiP in Desktop Mode transitions. At the moment, this is specifically tracking a PiP
- * transition for a task that is entering PiP via the minimize button on the caption bar.
- */
-class DesktopPipTransitionObserver {
- private val pendingPipTransitions = mutableMapOf<IBinder, PendingPipTransition>()
-
- /** Adds a pending PiP transition to be tracked. */
- fun addPendingPipTransition(transition: PendingPipTransition) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
- pendingPipTransitions[transition.token] = transition
- }
-
- /**
- * Called when any transition is ready, which may include transitions not tracked by this
- * observer.
- */
- fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
- val pipTransition = pendingPipTransitions.remove(transition) ?: return
-
- logD("Desktop PiP transition ready: %s", transition)
- for (change in info.changes) {
- val taskInfo = change.taskInfo
- if (taskInfo == null || taskInfo.taskId == -1) {
- continue
- }
-
- if (
- taskInfo.taskId == pipTransition.taskId &&
- taskInfo.windowingMode == WINDOWING_MODE_PINNED
- ) {
- logD("Desktop PiP transition was successful")
- pipTransition.onSuccess()
- return
- }
- }
- logD("Change with PiP task not found in Desktop PiP transition; likely failed")
- }
-
- /**
- * Data tracked for a pending PiP transition.
- *
- * @property token the PiP transition that is started.
- * @property taskId task id of the task entering PiP.
- * @property onSuccess callback to be invoked if the PiP transition is successful.
- */
- data class PendingPipTransition(val token: IBinder, val taskId: Int, val onSuccess: () -> Unit)
-
- private fun logD(msg: String, vararg arguments: Any?) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
- private companion object {
- private const val TAG = "DesktopPipTransitionObserver"
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b60fd5bb6c73..6214f329e0fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -214,7 +214,6 @@ class DesktopTasksController(
private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
private val desksOrganizer: DesksOrganizer,
private val desksTransitionObserver: DesksTransitionObserver,
- private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val userProfileContexts: UserProfileContexts,
private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
@@ -847,7 +846,6 @@ class DesktopTasksController(
}
val isMinimizingToPip =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
- desktopPipTransitionObserver.isPresent &&
(taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
// If task is going to PiP, start a PiP transition instead of a minimize transition
@@ -861,25 +859,23 @@ class DesktopTasksController(
/* displayChange= */ null,
/* flags= */ 0,
)
- val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
+ val requestRes =
+ transitions.dispatchRequest(SYNTHETIC_TRANSITION, requestInfo, /* skip= */ null)
wct.merge(requestRes.second, true)
- desktopPipTransitionObserver
- .get()
- .addPendingPipTransition(
- DesktopPipTransitionObserver.PendingPipTransition(
- token = freeformTaskTransitionStarter.startPipTransition(wct),
- taskId = taskInfo.taskId,
- onSuccess = {
- onDesktopTaskEnteredPip(
- taskId = taskId,
- deskId = deskId,
- displayId = taskInfo.displayId,
- taskIsLastVisibleTaskBeforePip = isLastTask,
- )
- },
+ // If the task minimizing to PiP is the last task, modify wct to perform Desktop cleanup
+ var desktopExitRunnable: RunOnTransitStart? = null
+ if (isLastTask) {
+ desktopExitRunnable =
+ performDesktopExitCleanUp(
+ wct = wct,
+ deskId = deskId,
+ displayId = displayId,
+ willExitDesktop = true,
)
- )
+ }
+ val transition = freeformTaskTransitionStarter.startPipTransition(wct)
+ desktopExitRunnable?.invoke(transition)
} else {
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
@@ -1893,11 +1889,7 @@ class DesktopTasksController(
displayId: Int,
forceExitDesktop: Boolean,
): Boolean {
- if (
- forceExitDesktop &&
- (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ||
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue)
- ) {
+ if (forceExitDesktop && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
// |forceExitDesktop| is true when the callers knows we'll exit desktop, such as when
// explicitly going fullscreen, so there's no point in checking the desktop state.
return true
@@ -1914,33 +1906,6 @@ class DesktopTasksController(
return true
}
- /** Potentially perform Desktop cleanup after a task successfully enters PiP. */
- @VisibleForTesting
- fun onDesktopTaskEnteredPip(
- taskId: Int,
- deskId: Int,
- displayId: Int,
- taskIsLastVisibleTaskBeforePip: Boolean,
- ) {
- if (
- !willExitDesktop(taskId, displayId, forceExitDesktop = taskIsLastVisibleTaskBeforePip)
- ) {
- return
- }
-
- val wct = WindowContainerTransaction()
- val desktopExitRunnable =
- performDesktopExitCleanUp(
- wct = wct,
- deskId = deskId,
- displayId = displayId,
- willExitDesktop = true,
- )
-
- val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
- desktopExitRunnable?.invoke(transition)
- }
-
private fun performDesktopExitCleanupIfNeeded(
taskId: Int,
deskId: Int? = null,
@@ -1964,7 +1929,7 @@ class DesktopTasksController(
}
/** TODO: b/394268248 - update [deskId] to be non-null. */
- private fun performDesktopExitCleanUp(
+ fun performDesktopExitCleanUp(
wct: WindowContainerTransaction,
deskId: Int?,
displayId: Int,
@@ -3967,6 +3932,12 @@ class DesktopTasksController(
DesktopTaskToFrontReason.TASKBAR_MANAGE_WINDOW ->
UnminimizeReason.TASKBAR_MANAGE_WINDOW
}
+
+ @JvmField
+ /**
+ * A placeholder for a synthetic transition that isn't backed by a true system transition.
+ */
+ val SYNTHETIC_TRANSITION: IBinder = Binder()
}
/** Defines interface for classes that can listen to changes for task resize. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index df4d18f8c803..3fd955d112f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -42,7 +42,6 @@ import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
-import java.util.Optional
/**
* A [Transitions.TransitionObserver] that observes shell transitions and updates the
@@ -55,7 +54,6 @@ class DesktopTasksTransitionObserver(
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
- private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val backAnimationController: BackAnimationController,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
shellInit: ShellInit,
@@ -97,7 +95,6 @@ class DesktopTasksTransitionObserver(
removeTaskIfNeeded(info)
}
removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
- desktopPipTransitionObserver.ifPresent { it.onTransitionReady(transition, info) }
}
private fun removeTaskIfNeeded(info: TransitionInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index 5a988fcd1b77..1effcdb20505 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -78,6 +78,9 @@ interface DesksOrganizer {
/** Whether the desk is activate according to the given change at the end of a transition. */
fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean
+ /** Allows for other classes to respond to task changes this organizer receives. */
+ fun setOnDesktopTaskInfoChangedListener(listener: (ActivityManager.RunningTaskInfo) -> Unit)
+
/** A callback that is invoked when the desk container is created. */
fun interface OnCreateCallback {
/** Calls back when the [deskId] has been created. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index 49ca58e7b32a..c30987ac7640 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -54,6 +54,7 @@ class RootTaskDesksOrganizer(
mutableListOf<CreateDeskMinimizationRootRequest>()
@VisibleForTesting
val deskMinimizationRootsByDeskId: MutableMap<Int, DeskMinimizationRoot> = mutableMapOf()
+ private var onTaskInfoChangedListener: ((RunningTaskInfo) -> Unit)? = null
init {
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -213,6 +214,10 @@ class RootTaskDesksOrganizer(
change.taskInfo?.isVisibleRequested == true &&
change.mode == TRANSIT_TO_FRONT
+ override fun setOnDesktopTaskInfoChangedListener(listener: (RunningTaskInfo) -> Unit) {
+ onTaskInfoChangedListener = listener
+ }
+
override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
handleTaskAppeared(taskInfo, leash)
updateLaunchAdjacentController()
@@ -220,6 +225,12 @@ class RootTaskDesksOrganizer(
override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
handleTaskInfoChanged(taskInfo)
+ if (
+ taskInfo.taskId !in deskRootsByDeskId &&
+ deskMinimizationRootsByDeskId.values.none { it.rootId == taskInfo.taskId }
+ ) {
+ onTaskInfoChangedListener?.invoke(taskInfo)
+ }
updateLaunchAdjacentController()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 5d8d8b685a23..51ef0ec60c3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -71,6 +71,7 @@ import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.desktopmode.DesktopPipTransitionController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -115,6 +116,7 @@ public class PipTransition extends PipTransitionController implements
private final DisplayController mDisplayController;
private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private final PipDesktopState mPipDesktopState;
+ private final Optional<DesktopPipTransitionController> mDesktopPipTransitionController;
private final PipInteractionHandler mPipInteractionHandler;
//
@@ -158,6 +160,7 @@ public class PipTransition extends PipTransitionController implements
DisplayController displayController,
Optional<SplitScreenController> splitScreenControllerOptional,
PipDesktopState pipDesktopState,
+ Optional<DesktopPipTransitionController> desktopPipTransitionController,
PipInteractionHandler pipInteractionHandler) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
pipBoundsAlgorithm);
@@ -172,6 +175,7 @@ public class PipTransition extends PipTransitionController implements
mDisplayController = displayController;
mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
mPipDesktopState = pipDesktopState;
+ mDesktopPipTransitionController = desktopPipTransitionController;
mPipInteractionHandler = pipInteractionHandler;
mExpandHandler = new PipExpandHandler(mContext, pipBoundsState, pipBoundsAlgorithm,
@@ -227,7 +231,18 @@ public class PipTransition extends PipTransitionController implements
@NonNull TransitionRequestInfo request) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
mEnterTransition = transition;
- return getEnterPipTransaction(transition, request.getPipChange());
+ final WindowContainerTransaction wct = getEnterPipTransaction(transition,
+ request.getPipChange());
+
+ mDesktopPipTransitionController.ifPresent(
+ desktopPipTransitionController ->
+ desktopPipTransitionController.handlePipTransition(
+ wct,
+ transition,
+ request.getPipChange().getTaskInfo()
+ )
+ );
+ return wct;
}
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index f6acca95916f..dc1b94e80ed7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -89,9 +89,6 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);
relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
- if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
- setCaptionVisibility(isCaptionVisible);
- }
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
@@ -100,19 +97,23 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
// Nothing is set up in this case including the decoration surface.
return;
}
+
if (mRootView != mResult.mRootView) {
mRootView = mResult.mRootView;
setupRootView(mResult.mRootView, mClickListener);
}
- }
- private void setCaptionVisibility(boolean visible) {
- if (mRootView == null) {
- return;
+ if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
+ setCaptionVisibility(mRootView, mRelayoutParams.mIsCaptionVisible);
}
+ }
+
+ private void setCaptionVisibility(@NonNull View rootView, boolean visible) {
final int v = visible ? View.VISIBLE : View.GONE;
- final View captionView = mRootView.findViewById(getCaptionViewId());
- captionView.setVisibility(v);
+ final View captionView = rootView.findViewById(getCaptionViewId());
+ if (captionView != null) {
+ captionView.setVisibility(v);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 0082d7971ad2..16fa5120d64b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -125,6 +125,7 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction;
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt;
import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.recents.RecentsTransitionStateListener;
@@ -210,6 +211,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
+ private final DesksOrganizer mDesksOrganizer;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -308,7 +310,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
- CompatUIHandler compatUI) {
+ CompatUIHandler compatUI,
+ DesksOrganizer desksOrganizer) {
this(
context,
shellExecutor,
@@ -356,7 +359,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
desktopModeCompatPolicy,
desktopTilingDecorViewModel,
multiDisplayDragMoveIndicatorController,
- compatUI);
+ compatUI,
+ desksOrganizer);
}
@VisibleForTesting
@@ -407,7 +411,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
- CompatUIHandler compatUI) {
+ CompatUIHandler compatUI,
+ DesksOrganizer desksOrganizer) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -485,6 +490,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mDesktopTasksController.setSnapEventHandler(this);
mMultiDisplayDragMoveIndicatorController = multiDisplayDragMoveIndicatorController;
mLatencyTracker = LatencyTracker.getInstance(mContext);
+ mDesksOrganizer = desksOrganizer;
shellInit.addInitCallback(this::onInit, this);
}
@@ -523,6 +529,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
});
}
mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ mDesksOrganizer.setOnDesktopTaskInfoChangedListener((taskInfo) -> {
+ onTaskInfoChanged(taskInfo);
+ return Unit.INSTANCE;
+ });
}
@Override
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index fa9864b539ee..7cd2bcc8efd4 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -52,7 +52,7 @@ abstract class SwitchAppByDoubleTapDividerBenchmark(override val flicker: Legacy
)
}
transitions {
- SplitScreenUtils.doubleTapDividerToSwitch(device)
+ SplitScreenUtils.doubleTapDividerToSwitch(device, instrumentation.uiAutomation)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
waitForLayersToSwitch(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
index 3fd93d3eaf59..dfc737174a4f 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -61,7 +61,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Test
open fun switchAppByDoubleTapDivider() {
- SplitScreenUtils.doubleTapDividerToSwitch(device)
+ SplitScreenUtils.doubleTapDividerToSwitch(device, instrumentation.uiAutomation)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
waitForLayersToSwitch(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index e4183f16ba14..e54930d730f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.utils
import android.app.Instrumentation
+import android.app.UiAutomation
import android.content.Context
import android.graphics.Point
import android.os.SystemClock
@@ -355,13 +356,40 @@ object SplitScreenUtils {
)
}
- fun doubleTapDividerToSwitch(device: UiDevice) {
+ fun doubleTapDividerToSwitch(device: UiDevice, uiAutomation: UiAutomation) {
val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- val interval =
- (ViewConfiguration.getDoubleTapTimeout() + ViewConfiguration.getDoubleTapMinTime()) / 2
- dividerBar.click()
- SystemClock.sleep(interval.toLong())
- dividerBar.click()
+ val x = dividerBar.visibleCenter.x.toFloat()
+ val y = dividerBar.visibleCenter.y.toFloat()
+
+ // To send a double-tap action, we set a DOWN event, then UP, then DOWN, then, UP.
+ val startTime = SystemClock.uptimeMillis()
+ val timeOfFirstUp = startTime + ViewConfiguration.getTapTimeout()
+ // Between the two taps, we wait an arbitrary amount of time between the min and max times
+ // for a double-tap.
+ val timeOfSecondDown = timeOfFirstUp + ViewConfiguration.getDoubleTapMinTime() +
+ ((ViewConfiguration.getDoubleTapTimeout() -
+ ViewConfiguration.getDoubleTapMinTime()) / 4)
+ val timeOfSecondUp = timeOfSecondDown + ViewConfiguration.getTapTimeout()
+
+ val downEvent = MotionEvent.obtain(startTime, startTime, MotionEvent.ACTION_DOWN, x, y,
+ 0 /* metaState */)
+ downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(downEvent, true)
+
+ val upEvent = MotionEvent.obtain(startTime, timeOfFirstUp, MotionEvent.ACTION_UP, x, y,
+ 0 /* metaState */)
+ upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(upEvent, true)
+
+ val downEvent2 = MotionEvent.obtain(timeOfSecondDown, timeOfSecondDown,
+ MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */)
+ downEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(downEvent2, true)
+
+ val upEvent2 = MotionEvent.obtain(timeOfSecondDown, timeOfSecondUp, MotionEvent.ACTION_UP,
+ x, y, 0 /* metaState */)
+ upEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(upEvent2, true)
}
fun copyContentInSplit(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 96b826f93aae..488025a3d754 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -104,7 +104,9 @@ class DesktopDisplayModeControllerTest(
private val wallpaperToken = MockToken().token()
private val defaultDisplay = mock<Display>()
private val externalDisplay = mock<Display>()
- private val mouseDevice = mock<InputDevice>()
+ private val touchpadDevice = mock<InputDevice>()
+ private val keyboardDevice = mock<InputDevice>()
+ private val connectedDeviceIds = mutableListOf<Int>()
private lateinit var extendedDisplaySettingsRestoreSession:
ExtendedDisplaySettingsRestoreSession
@@ -145,16 +147,18 @@ class DesktopDisplayModeControllerTest(
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
- setTabletModeStatus(SwitchState.UNKNOWN)
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- defaultDisplay
- )
- ).thenReturn(true)
- whenever(mouseDevice.supportsSource(InputDevice.SOURCE_MOUSE)).thenReturn(true)
- whenever(inputManager.getInputDevice(EXTERNAL_DEVICE_ID)).thenReturn(mouseDevice)
- setMouseConnected(false)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, defaultDisplay))
+ .thenReturn(true)
+ whenever(touchpadDevice.supportsSource(InputDevice.SOURCE_TOUCHPAD)).thenReturn(true)
+ whenever(touchpadDevice.isEnabled()).thenReturn(true)
+ whenever(inputManager.getInputDevice(TOUCHPAD_DEVICE_ID)).thenReturn(touchpadDevice)
+ whenever(keyboardDevice.isFullKeyboard()).thenReturn(true)
+ whenever(keyboardDevice.isVirtual()).thenReturn(false)
+ whenever(keyboardDevice.isEnabled()).thenReturn(true)
+ whenever(inputManager.getInputDevice(KEYBOARD_DEVICE_ID)).thenReturn(keyboardDevice)
+ whenever(inputManager.inputDeviceIds).thenAnswer { connectedDeviceIds.toIntArray() }
+ setTouchpadConnected(false)
+ setKeyboardConnected(false)
}
@After
@@ -211,8 +215,8 @@ class DesktopDisplayModeControllerTest(
@DisableFlags(Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH)
fun testTargetWindowingMode_formfactorDisabled(
@TestParameter param: ExternalDisplayBasedTargetModeTestCase,
- @TestParameter tabletModeStatus: SwitchState,
- @TestParameter hasAnyMouseDevice: Boolean,
+ @TestParameter hasAnyTouchpadDevice: Boolean,
+ @TestParameter hasAnyKeyboardDevice: Boolean,
) {
whenever(mockWindowManager.getWindowingMode(anyInt()))
.thenReturn(param.defaultWindowingMode)
@@ -221,15 +225,11 @@ class DesktopDisplayModeControllerTest(
} else {
disconnectExternalDisplay()
}
- setTabletModeStatus(tabletModeStatus)
- setMouseConnected(hasAnyMouseDevice)
+ setTouchpadConnected(hasAnyTouchpadDevice)
+ setKeyboardConnected(hasAnyKeyboardDevice)
setExtendedMode(param.extendedDisplayEnabled)
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- defaultDisplay
- )
- ).thenReturn(param.isDefaultDisplayDesktopEligible)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, defaultDisplay))
+ .thenReturn(param.isDefaultDisplayDesktopEligible)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -246,15 +246,11 @@ class DesktopDisplayModeControllerTest(
} else {
disconnectExternalDisplay()
}
- setTabletModeStatus(param.tabletModeStatus)
setExtendedMode(param.extendedDisplayEnabled)
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- defaultDisplay
- )
- ).thenReturn(param.isDefaultDisplayDesktopEligible)
- setMouseConnected(param.hasAnyMouseDevice)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, defaultDisplay))
+ .thenReturn(param.isDefaultDisplayDesktopEligible)
+ setTouchpadConnected(param.hasAnyTouchpadDevice)
+ setKeyboardConnected(param.hasAnyKeyboardDevice)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -308,18 +304,10 @@ class DesktopDisplayModeControllerTest(
controller.refreshDisplayWindowingMode()
}
- private fun setTabletModeStatus(status: SwitchState) {
- whenever(inputManager.isInTabletMode()).thenReturn(status.value)
- }
-
private fun setExtendedMode(enabled: Boolean) {
if (DisplayFlags.enableDisplayContentModeManagement()) {
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- externalDisplay
- )
- ).thenReturn(enabled)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay))
+ .thenReturn(enabled)
} else {
Settings.Global.putInt(
context.contentResolver,
@@ -329,9 +317,20 @@ class DesktopDisplayModeControllerTest(
}
}
- private fun setMouseConnected(connected: Boolean) {
- whenever(inputManager.inputDeviceIds)
- .thenReturn(if (connected) intArrayOf(EXTERNAL_DEVICE_ID) else intArrayOf())
+ private fun setTouchpadConnected(connected: Boolean) {
+ if (connected) {
+ connectedDeviceIds.add(TOUCHPAD_DEVICE_ID)
+ } else {
+ connectedDeviceIds.remove(TOUCHPAD_DEVICE_ID)
+ }
+ }
+
+ private fun setKeyboardConnected(connected: Boolean) {
+ if (connected) {
+ connectedDeviceIds.add(KEYBOARD_DEVICE_ID)
+ } else {
+ connectedDeviceIds.remove(KEYBOARD_DEVICE_ID)
+ }
}
private class ExtendedDisplaySettingsRestoreSession(
@@ -358,13 +357,8 @@ class DesktopDisplayModeControllerTest(
companion object {
const val EXTERNAL_DISPLAY_ID = 100
- const val EXTERNAL_DEVICE_ID = 10
-
- enum class SwitchState(val value: Int) {
- UNKNOWN(InputManager.SWITCH_STATE_UNKNOWN),
- ON(InputManager.SWITCH_STATE_ON),
- OFF(InputManager.SWITCH_STATE_OFF),
- }
+ const val TOUCHPAD_DEVICE_ID = 10
+ const val KEYBOARD_DEVICE_ID = 11
enum class ExternalDisplayBasedTargetModeTestCase(
val defaultWindowingMode: Int,
@@ -490,393 +484,265 @@ class DesktopDisplayModeControllerTest(
enum class FormFactorBasedTargetModeTestCase(
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
- val tabletModeStatus: SwitchState,
val isDefaultDisplayDesktopEligible: Boolean,
- val hasAnyMouseDevice: Boolean,
+ val hasAnyTouchpadDevice: Boolean,
+ val hasAnyKeyboardDevice: Boolean,
val expectedWindowingMode: Int,
) {
- EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt
new file mode 100644
index 000000000000..47a9a6c8d840
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.pip.PipDesktopState
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+/**
+ * Tests for [DesktopPipTransitionController].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopPipTransitionControllerTest
+ */
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+class DesktopPipTransitionControllerTest(flags: FlagsParameterization) : ShellTestCase() {
+ private val mockDesktopTasksController = mock<DesktopTasksController>()
+ private val mockDesktopUserRepositories = mock<DesktopUserRepositories>()
+ private val mockDesktopRepository = mock<DesktopRepository>()
+ private val mockPipDesktopState = mock<PipDesktopState>()
+
+ private lateinit var controller: DesktopPipTransitionController
+
+ private val transition = Binder()
+ private val wct = WindowContainerTransaction()
+ private val taskInfo = createFreeformTask()
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setUp() {
+ whenever(mockPipDesktopState.isDesktopWindowingPipEnabled()).thenReturn(true)
+ whenever(mockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mockDesktopRepository)
+ whenever(mockDesktopRepository.isAnyDeskActive(anyInt())).thenReturn(true)
+ whenever(mockDesktopRepository.getActiveDeskId(anyInt())).thenReturn(DESK_ID)
+
+ controller =
+ DesktopPipTransitionController(
+ mockDesktopTasksController,
+ mockDesktopUserRepositories,
+ mockPipDesktopState,
+ )
+ }
+
+ @Test
+ fun handlePipTransition_noDeskActive_doesntPerformDesktopExitCleanup() {
+ whenever(mockDesktopRepository.isAnyDeskActive(eq(taskInfo.displayId))).thenReturn(false)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = false)
+ }
+
+ @Test
+ fun handlePipTransition_notLastTask_doesntPerformDesktopExitCleanup() {
+ whenever(
+ mockDesktopRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = eq(taskInfo.taskId),
+ deskId = eq(DESK_ID),
+ displayId = eq(taskInfo.displayId),
+ )
+ )
+ .thenReturn(false)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = false)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handlePipTransition_noActiveDeskId_multiDesk_doesntPerformDesktopExitCleanup() {
+ whenever(mockDesktopRepository.getActiveDeskId(eq(taskInfo.displayId))).thenReturn(null)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = false)
+ }
+
+ @Test
+ fun handlePipTransition_isLastTask_performDesktopExitCleanup() {
+ whenever(
+ mockDesktopRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = eq(taskInfo.taskId),
+ deskId = eq(DESK_ID),
+ displayId = eq(taskInfo.displayId),
+ )
+ )
+ .thenReturn(true)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = true)
+ }
+
+ private fun verifyPerformDesktopExitCleanupAfterPip(isCalled: Boolean) {
+ if (isCalled) {
+ verify(mockDesktopTasksController)
+ .performDesktopExitCleanUp(
+ wct = wct,
+ deskId = DESK_ID,
+ displayId = DEFAULT_DISPLAY,
+ willExitDesktop = true,
+ )
+ } else {
+ verify(mockDesktopTasksController, never())
+ .performDesktopExitCleanUp(
+ any(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyBoolean(),
+ )
+ }
+ }
+
+ private companion object {
+ const val DESK_ID = 1
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> =
+ FlagsParameterization.allCombinationsOf(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
deleted file mode 100644
index ef394d81cc57..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.desktopmode
-
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
-import android.os.Binder
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
-import android.view.WindowManager.TRANSIT_PIP
-import android.window.TransitionInfo
-import androidx.test.filters.SmallTest
-import com.android.window.flags.Flags
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.TestRunningTaskInfoBuilder
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
-
-/**
- * Tests for [DesktopPipTransitionObserver].
- *
- * Build/Install/Run: atest WMShellUnitTests:DesktopPipTransitionObserverTest
- */
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class DesktopPipTransitionObserverTest : ShellTestCase() {
-
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
- private lateinit var observer: DesktopPipTransitionObserver
-
- private val transition = Binder()
- private var onSuccessInvokedCount = 0
-
- @Before
- fun setUp() {
- observer = DesktopPipTransitionObserver()
-
- onSuccessInvokedCount = 0
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onTransitionReady_taskInPinnedWindowingMode_onSuccessInvoked() {
- val taskId = 1
- val pipTransition = createPendingPipTransition(taskId)
- val successfulChange = createChange(taskId, WINDOWING_MODE_PINNED)
- observer.addPendingPipTransition(pipTransition)
-
- observer.onTransitionReady(
- transition = transition,
- info = TransitionInfo(
- TRANSIT_PIP, /* flags= */
- 0
- ).apply { addChange(successfulChange) },
- )
-
- assertThat(onSuccessInvokedCount).isEqualTo(1)
- }
-
- private fun createPendingPipTransition(
- taskId: Int
- ): DesktopPipTransitionObserver.PendingPipTransition {
- return DesktopPipTransitionObserver.PendingPipTransition(
- token = transition,
- taskId = taskId,
- onSuccess = { onSuccessInvokedCount += 1 },
- )
- }
-
- private fun createChange(taskId: Int, windowingMode: Int): TransitionInfo.Change {
- return TransitionInfo.Change(mock(), mock()).apply {
- taskInfo =
- TestRunningTaskInfoBuilder()
- .setTaskId(taskId)
- .setWindowingMode(windowingMode)
- .build()
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index b8c2273e1465..bcdff11363ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -265,7 +265,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Mock private lateinit var desksOrganizer: DesksOrganizer
@Mock private lateinit var userProfileContexts: UserProfileContexts
@Mock private lateinit var desksTransitionsObserver: DesksTransitionObserver
- @Mock private lateinit var desktopPipTransitionObserver: DesktopPipTransitionObserver
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var mockDisplayContext: Context
@Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
@@ -460,7 +459,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionsObserver,
- Optional.of(desktopPipTransitionObserver),
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -3540,15 +3538,20 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
.addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
}
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
- val task = setUpPipTask(autoEnterEnabled = true)
+ private fun minimizePipTask(task: RunningTaskInfo) {
val handler = mock(TransitionHandler::class.java)
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+
+ minimizePipTask(task)
verify(freeformTaskTransitionStarter).startPipTransition(any())
verify(freeformTaskTransitionStarter, never())
@@ -3568,7 +3571,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
)
.thenReturn(Binder())
- controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+ minimizePipTask(task)
verify(freeformTaskTransitionStarter)
.startMinimizedModeTransition(any(), eq(task.taskId), anyBoolean())
@@ -3579,52 +3582,24 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
fun onPipTaskMinimize_autoEnterEnabled_sendsTaskbarRoundingUpdate() {
val task = setUpPipTask(autoEnterEnabled = true)
- val handler = mock(TransitionHandler::class.java)
- whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
- .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+ minimizePipTask(task)
verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
}
@Test
@EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
- )
- fun onDesktopTaskEnteredPip_pipIsLastTask_removesWallpaper() {
- val task = setUpPipTask(autoEnterEnabled = true)
-
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = DEFAULT_DISPLAY,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = true,
- )
-
- // Wallpaper is moved to the back
- val wct = getLatestTransition()
- wct.assertReorder(wallpaperToken, /* toTop= */ false)
- }
-
- @Test
- @EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
- fun onDesktopTaskEnteredPip_pipIsLastTask_deactivatesDesk() {
+ fun onPipTaskMinimize_isLastTask_deactivatesDesk() {
val deskId = DEFAULT_DISPLAY
val task = setUpPipTask(autoEnterEnabled = true, deskId = deskId)
val transition = Binder()
- whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+ whenever(freeformTaskTransitionStarter.startPipTransition(any())).thenReturn(transition)
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = deskId,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = true,
- )
+ minimizePipTask(task)
verify(desksOrganizer).deactivateDesk(any(), eq(deskId))
verify(desksTransitionsObserver)
@@ -3632,44 +3607,31 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onDesktopTaskEnteredPip_pipIsLastTask_launchesHome() {
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun onPipTaskMinimize_isLastTask_removesWallpaper() {
val task = setUpPipTask(autoEnterEnabled = true)
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = DEFAULT_DISPLAY,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = true,
- )
+ minimizePipTask(task)
- val wct = getLatestTransition()
- wct.assertPendingIntent(launchHomeIntent(DEFAULT_DISPLAY))
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(freeformTaskTransitionStarter).startPipTransition(arg.capture())
+ // Wallpaper is moved to the back
+ arg.lastValue.assertReorder(wallpaperToken, /* toTop= */ false)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onDesktopTaskEnteredPip_pipIsNotLastTask_doesntExitDesktopMode() {
+ fun onPipTaskMinimize_isLastTask_launchesHome() {
val task = setUpPipTask(autoEnterEnabled = true)
- val deskId = DEFAULT_DISPLAY
- setUpFreeformTask(deskId = deskId) // launch another freeform task
- val transition = Binder()
- whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = deskId,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = false,
- )
+ minimizePipTask(task)
- // No transition to exit Desktop mode is started
- verifyWCTNotExecuted()
- verify(desktopModeEnterExitTransitionListener, never())
- .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- verify(desksOrganizer, never()).deactivateDesk(any(), eq(deskId))
- verify(desksTransitionsObserver, never())
- .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(freeformTaskTransitionStarter).startPipTransition(arg.capture())
+ arg.lastValue.assertPendingIntent(launchHomeIntent(DEFAULT_DISPLAY))
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 5ef1ace7873d..1e0c94c2452c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -50,7 +50,6 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import java.util.Optional
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -86,7 +85,6 @@ class DesktopTasksTransitionObserverTest {
private val userRepositories = mock<DesktopUserRepositories>()
private val taskRepository = mock<DesktopRepository>()
private val mixedHandler = mock<DesktopMixedTransitionHandler>()
- private val pipTransitionObserver = mock<DesktopPipTransitionObserver>()
private val backAnimationController = mock<BackAnimationController>()
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
@@ -111,7 +109,6 @@ class DesktopTasksTransitionObserverTest {
transitions,
shellTaskOrganizer,
mixedHandler,
- Optional.of(pipTransitionObserver),
backAnimationController,
desktopWallpaperActivityTokenProvider,
shellInit,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 9af504797182..e57fc38e3607 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.desktopmode.multidesks
+import android.app.ActivityManager
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.testing.AndroidTestingRunner
@@ -48,7 +49,9 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -67,6 +70,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
private val mockShellCommandHandler = mock<ShellCommandHandler>()
private val mockShellTaskOrganizer = mock<ShellTaskOrganizer>()
private val launchAdjacentController = LaunchAdjacentController(mock())
+ private val taskInfoChangedListener = mock<(ActivityManager.RunningTaskInfo) -> Unit>()
private lateinit var organizer: RootTaskDesksOrganizer
@@ -79,6 +83,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
mockShellTaskOrganizer,
launchAdjacentController,
)
+ organizer.setOnDesktopTaskInfoChangedListener(taskInfoChangedListener)
}
@Test fun testCreateDesk_createsDeskAndMinimizationRoots() = runTest { createDesk() }
@@ -652,6 +657,34 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
}
+ @Test
+ fun onTaskInfoChanged_taskNotRoot_invokesListener() = runTest {
+ createDesk()
+ val task = createFreeformTask().apply { taskId = TEST_CHILD_TASK_ID }
+
+ organizer.onTaskInfoChanged(task)
+
+ verify(taskInfoChangedListener).invoke(task)
+ }
+
+ @Test
+ fun onTaskInfoChanged_isDeskRoot_doesNotInvokeListener() = runTest {
+ val deskRoot = createDesk().deskRoot
+
+ organizer.onTaskInfoChanged(deskRoot.taskInfo)
+
+ verify(taskInfoChangedListener, never()).invoke(any())
+ }
+
+ @Test
+ fun onTaskInfoChanged_isMinimizationRoot_doesNotInvokeListener() = runTest {
+ val minimizationRoot = createDesk().minimizationRoot
+
+ organizer.onTaskInfoChanged(minimizationRoot.taskInfo)
+
+ verify(taskInfoChangedListener, never()).invoke(any())
+ }
+
private data class DeskRoots(
val deskRoot: DeskRoot,
val minimizationRoot: DeskMinimizationRoot,
@@ -712,4 +745,8 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
hop.newParent == desk.deskRoot.token.asBinder() &&
hop.toTop
}
+
+ companion object {
+ private const val TEST_CHILD_TASK_ID = 100
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 80dcd7d69f00..23994a2bd547 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -67,6 +67,7 @@ import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
import com.android.wm.shell.desktopmode.education.AppHandleEducationController
import com.android.wm.shell.desktopmode.education.AppToWebEducationController
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
@@ -146,6 +147,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockMultiDisplayDragMoveIndicatorController =
mock<MultiDisplayDragMoveIndicatorController>()
protected val mockCompatUIHandler = mock<CompatUIHandler>()
+ protected val mockDesksOrganizer = mock<DesksOrganizer>()
protected val mockInputManager = mock<InputManager>()
private val mockTaskPositionerFactory =
mock<DesktopModeWindowDecorViewModel.TaskPositionerFactory>()
@@ -246,6 +248,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockTilingWindowDecoration,
mockMultiDisplayDragMoveIndicatorController,
mockCompatUIHandler,
+ mockDesksOrganizer
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 9cc58ae35692..4b460c6ab039 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -199,6 +199,16 @@ flag {
}
flag {
+ name: "fix_is_in_emergency_anr"
+ namespace: "location"
+ description: "Avoid calling IPC with a lock to avoid deadlock"
+ bug: "355384257"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "gnss_assistance_interface_jni"
namespace: "location"
description: "Flag for GNSS assistance interface JNI"
diff --git a/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
index 0191ea786de0..b5afa6afa5e0 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
@@ -32,7 +32,7 @@ import android.media.quality.SoundProfile;
*/
interface IMediaQualityManager {
// TODO: use UserHandle
- PictureProfile createPictureProfile(in PictureProfile pp, int userId);
+ void createPictureProfile(in PictureProfile pp, int userId);
void updatePictureProfile(in String id, in PictureProfile pp, int userId);
void removePictureProfile(in String id, int userId);
boolean setDefaultPictureProfile(in String id, int userId);
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 60f209b47482..574671376e2e 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -106,16 +106,13 @@
<!-- Description of the helper dialog for NEARBY_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
<string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream apps from your <xliff:g id="device_type" example="phone">%3$s</xliff:g></string>
- <!-- ================= DEVICE_PROFILE_SENSOR_DEVICE_STREAMING ================= -->
+ <!-- ================= DEVICE_PROFILE_VIRTUAL_DEVICE ================= -->
- <!-- Confirmation for associating an application with a companion device of SENSOR_DEVICE_STREAMING profile (type) [CHAR LIMIT=NONE] -->
- <string name="title_sensor_device_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream audio and system features between your <xliff:g id="device_type" example="phone">%2$s</xliff:g> and &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
+ <!-- Confirmation for associating an application with a companion device of VIRTUAL_DEVICE profile (type) [CHAR LIMIT=NONE] -->
+ <string name="title_virtual_device">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream audio and system features between your <xliff:g id="device_type" example="phone">%3$s</xliff:g> and &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%2$s</xliff:g>&lt;/strong&gt;?</string>
- <!-- Summary for associating an application with a companion device of SENSOR_DEVICE_STREAMING profile [CHAR LIMIT=NONE] -->
- <string name="summary_sensor_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s played on your <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream audio to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
-
- <!-- Description of the helper dialog for SENSOR_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
- <string name="helper_summary_sensor_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream audio and system features between your devices.</string>
+ <!-- Summary for associating an application with a companion device of VIRTUAL_DEVICE profile [CHAR LIMIT=NONE] -->
+ <string name="summary_virtual_device"><xliff:g id="app_name" example="Exo">%2$s</xliff:g> will have access to anything that’s played on <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%2$s</xliff:g> will be able to stream audio to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
<!-- ================= null profile ================= -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index 518757dd0d5c..c07e572eb649 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -667,7 +667,8 @@ public class CompanionAssociationActivity extends FragmentActivity implements
final int summaryResourceId = PROFILE_SUMMARIES.get(deviceProfile);
final String remoteDeviceName = mSelectedDevice.getDisplayName();
final Spanned title = getHtmlFromResources(
- this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName);
+ this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName,
+ getString(R.string.device_type));
final Spanned summary;
if (deviceProfile == null && mRequest.isSingleDevice()) {
@@ -675,7 +676,8 @@ public class CompanionAssociationActivity extends FragmentActivity implements
mConstraintList.setVisibility(View.GONE);
} else {
summary = getHtmlFromResources(
- this, summaryResourceId, getString(R.string.device_type));
+ this, summaryResourceId, getString(R.string.device_type), mAppLabel,
+ remoteDeviceName);
setupPermissionList(deviceProfile);
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
index f756a6235c14..f6e680207530 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
@@ -21,7 +21,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_VIRTUAL_DEVICE;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -118,7 +118,7 @@ final class CompanionDeviceResources {
map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, R.string.title_automotive_projection);
map.put(DEVICE_PROFILE_COMPUTER, R.string.title_computer);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.title_nearby_device_streaming);
- map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING, R.string.title_sensor_device_streaming);
+ map.put(DEVICE_PROFILE_VIRTUAL_DEVICE, R.string.title_virtual_device);
map.put(DEVICE_PROFILE_WATCH, R.string.confirmation_title);
map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title_glasses);
map.put(null, R.string.confirmation_title);
@@ -133,7 +133,7 @@ final class CompanionDeviceResources {
map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses);
map.put(DEVICE_PROFILE_APP_STREAMING, R.string.summary_app_streaming);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.summary_nearby_device_streaming);
- map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING, R.string.summary_sensor_device_streaming);
+ map.put(DEVICE_PROFILE_VIRTUAL_DEVICE, R.string.summary_virtual_device);
map.put(null, R.string.summary_generic);
PROFILE_SUMMARIES = unmodifiableMap(map);
@@ -145,8 +145,6 @@ final class CompanionDeviceResources {
map.put(DEVICE_PROFILE_APP_STREAMING, R.string.helper_summary_app_streaming);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
R.string.helper_summary_nearby_device_streaming);
- map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
- R.string.helper_summary_sensor_device_streaming);
map.put(DEVICE_PROFILE_COMPUTER, R.string.helper_summary_computer);
PROFILE_HELPER_SUMMARIES = unmodifiableMap(map);
@@ -178,6 +176,7 @@ final class CompanionDeviceResources {
final Map<String, Integer> map = new ArrayMap<>();
map.put(DEVICE_PROFILE_WATCH, R.string.profile_name_watch);
map.put(DEVICE_PROFILE_GLASSES, R.string.profile_name_glasses);
+ map.put(DEVICE_PROFILE_VIRTUAL_DEVICE, R.string.profile_name_generic);
map.put(null, R.string.profile_name_generic);
PROFILE_NAMES = unmodifiableMap(map);
@@ -188,6 +187,7 @@ final class CompanionDeviceResources {
final Map<String, Integer> map = new ArrayMap<>();
map.put(DEVICE_PROFILE_WATCH, R.drawable.ic_watch);
map.put(DEVICE_PROFILE_GLASSES, R.drawable.ic_glasses);
+ map.put(DEVICE_PROFILE_VIRTUAL_DEVICE, R.drawable.ic_device_other);
map.put(null, R.drawable.ic_device_other);
PROFILE_ICONS = unmodifiableMap(map);
@@ -198,6 +198,7 @@ final class CompanionDeviceResources {
final Set<String> set = new ArraySet<>();
set.add(DEVICE_PROFILE_WATCH);
set.add(DEVICE_PROFILE_GLASSES);
+ set.add(DEVICE_PROFILE_VIRTUAL_DEVICE);
set.add(null);
SUPPORTED_PROFILES = unmodifiableSet(set);
@@ -210,7 +211,6 @@ final class CompanionDeviceResources {
set.add(DEVICE_PROFILE_COMPUTER);
set.add(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION);
set.add(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
- set.add(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
set.add(DEVICE_PROFILE_WEARABLE_SENSING);
set.add(null);
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_filled.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_filled.xml
new file mode 100644
index 000000000000..68cc058c5974
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_filled.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/settingslib_materialColorPrimary"/>
+ <corners android:radius="@dimen/settingslib_expressive_radius_full"/>
+</shape> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_outline.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_outline.xml
new file mode 100644
index 000000000000..213289d5158b
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_button_background_outline.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke android:width="1dp"
+ android:color="@color/settingslib_materialColorOutlineVariant"/>
+ <corners android:radius="@dimen/settingslib_expressive_radius_full"/>
+</shape> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
index 3af88c48e8ca..de48f99215fb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
@@ -90,4 +90,39 @@
parent="@style/TextAppearance.SettingsLib.BodyMedium">
<item name="android:textColor">@color/settingslib_text_color_secondary</item>
</style>
+
+ <style name="Widget.SettingsLib.DialogWindowTitle" parent="">
+ <item name="android:scrollHorizontally">false</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.HeadlineSmall</item>
+ </style>
+
+ <style name="Widget.SettingsLib.ButtonBar" parent="@style/Widget.AppCompat.ButtonBar.AlertDialog">
+ <item name="android:paddingTop">@dimen/settingslib_expressive_space_extrasmall2</item>
+ <item name="android:paddingBottom">@dimen/settingslib_expressive_space_small4</item>
+ </style>
+
+ <style name="Widget.SettingsLib.DialogButton" parent="@style/Widget.AppCompat.Button">
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:minHeight">0dp</item>
+ <item name="android:minWidth">0dp</item>
+ <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+ <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:stateListAnimator">@null</item>
+ <item name="textAllCaps">false</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item>
+ <item name="android:textColor">@color/settingslib_materialColorPrimary</item>
+ <item name="android:background">@android:color/transparent</item>
+ </style>
+
+ <style name="Widget.SettingsLib.DialogButton.Filled">
+ <item name="android:layout_marginEnd">@dimen/settingslib_expressive_space_extrasmall6</item>
+ <item name="android:background">@drawable/settingslib_expressive_button_background_filled</item>
+ <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
+ </style>
+
+ <style name="Widget.SettingsLib.DialogButton.Outline">
+ <item name="android:layout_marginEnd">@dimen/settingslib_expressive_space_extrasmall4</item>
+ <item name="android:background">@drawable/settingslib_expressive_button_background_outline</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
index 14f214a96435..5173ebeaa9a1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
@@ -53,6 +53,17 @@
<item name="colorControlNormal">?android:attr/colorControlNormal</item>
<!-- For AndroidX AlertDialog -->
- <!--item name="alertDialogTheme">@style/Theme.AlertDialog.SettingsLib</item-->
+ <item name="alertDialogTheme">@style/Theme.AlertDialog.SettingsLib.Expressive</item>
+ </style>
+
+ <style name="Theme.AlertDialog.SettingsLib.Expressive">
+ <item name="colorAccent">@color/settingslib_materialColorPrimary</item>
+ <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerHigh</item>
+ <item name="android:windowTitleStyle">@style/Widget.SettingsLib.DialogWindowTitle</item>
+ <item name="dialogPreferredPadding">@dimen/settingslib_expressive_space_small4</item>
+ <item name="buttonBarStyle">@style/Widget.SettingsLib.ButtonBar</item>
+ <item name="buttonBarPositiveButtonStyle">@style/Widget.SettingsLib.DialogButton.Filled</item>
+ <item name="buttonBarNegativeButtonStyle">@style/Widget.SettingsLib.DialogButton.Outline</item>
+ <item name="buttonBarNeutralButtonStyle">@style/Widget.SettingsLib.DialogButton</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 527a1f16a84f..5bbfdf7bab81 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -672,6 +672,7 @@ public class SettingsHelper {
public static LocaleList resolveLocales(LocaleList restore, LocaleList current,
String[] supportedLocales) {
final HashMap<Locale, Locale> allLocales = new HashMap<>(supportedLocales.length);
+ final HashSet<String> existingLanguageAndScript = new HashSet<>();
for (String supportedLocaleStr : supportedLocales) {
final Locale locale = Locale.forLanguageTag(supportedLocaleStr);
allLocales.put(toFullLocale(locale), locale);
@@ -679,30 +680,26 @@ public class SettingsHelper {
// After restoring to reset locales, need to get extensions from restored locale. Get the
// first restored locale to check its extension.
- final Locale restoredLocale = restore.isEmpty()
+ final Locale firstRestoredLocale = restore.isEmpty()
? Locale.ROOT
: restore.get(0);
final ArrayList<Locale> filtered = new ArrayList<>(current.size());
for (int i = 0; i < current.size(); i++) {
- Locale locale = copyExtensionToTargetLocale(restoredLocale, current.get(i));
- allLocales.remove(toFullLocale(locale));
- filtered.add(locale);
+ Locale locale = copyExtensionToTargetLocale(firstRestoredLocale, current.get(i));
+
+ if (locale != null && existingLanguageAndScript.add(getLanguageAndScript(locale))) {
+ allLocales.remove(toFullLocale(locale));
+ filtered.add(locale);
+ }
}
- final HashSet<String> existingLanguageAndScript = new HashSet<>();
for (int i = 0; i < restore.size(); i++) {
- final Locale restoredLocaleWithExtension = copyExtensionToTargetLocale(restoredLocale,
- getFilteredLocale(restore.get(i), allLocales));
-
- if (restoredLocaleWithExtension != null) {
- String language = restoredLocaleWithExtension.getLanguage();
- String script = restoredLocaleWithExtension.getScript();
+ final Locale restoredLocaleWithExtension = copyExtensionToTargetLocale(
+ firstRestoredLocale, getFilteredLocale(restore.get(i), allLocales));
- String restoredLanguageAndScript =
- script == null ? language : language + "-" + script;
- if (existingLanguageAndScript.add(restoredLanguageAndScript)) {
- filtered.add(restoredLocaleWithExtension);
- }
+ if (restoredLocaleWithExtension != null && existingLanguageAndScript.add(
+ getLanguageAndScript(restoredLocaleWithExtension))) {
+ filtered.add(restoredLocaleWithExtension);
}
}
@@ -713,6 +710,16 @@ public class SettingsHelper {
return new LocaleList(filtered.toArray(new Locale[filtered.size()]));
}
+ private static String getLanguageAndScript(Locale locale) {
+ if (locale == null) {
+ return "";
+ }
+
+ String language = locale.getLanguage();
+ String script = locale.getScript();
+ return script == null ? language : String.join("-", language, script);
+ }
+
private static Locale copyExtensionToTargetLocale(Locale restoredLocale,
Locale targetLocale) {
if (!restoredLocale.hasExtensions()) {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 48c778542d66..2160d3164b17 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -388,11 +388,18 @@ public class SettingsHelperTest {
LocaleList.forLanguageTags("zh-Hant-TW"), // current
new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" })); // supported
- assertEquals(LocaleList.forLanguageTags("en-US,zh-Hans-TW"),
+ assertEquals(LocaleList.forLanguageTags("en-US,zh-Hans-TW,fr-FR"),
SettingsHelper.resolveLocales(
- LocaleList.forLanguageTags("en-UK,en-GB,zh-Hans-HK"), // restore
- LocaleList.forLanguageTags("en-US,zh-Hans-TW"), // current
- new String[] { "en-US,zh-Hans-TW,en-UK,en-GB,zh-Hans-HK" })); // supported
+ // restore
+ LocaleList.forLanguageTags("en-UK,en-GB,zh-Hans-HK,fr-FR"),
+
+ // current
+ LocaleList.forLanguageTags("en-US,zh-Hans-TW"),
+
+ // supported
+ new String[] {
+ "en-US" , "zh-Hans-TW" , "en-UK", "en-GB", "zh-Hans-HK", "fr-FR"
+ }));
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 758ad797f761..a178869d23d6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -349,7 +349,7 @@
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING" />
- <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a93291f2db98..17d8bbfd2240 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -542,6 +542,16 @@ flag {
}
flag {
+ name: "status_bar_window_no_custom_touch"
+ namespace: "systemui"
+ description: "Don't have any custom touch handling in StatusBarWindowView"
+ bug: "391894499"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "icon_refresh_2025"
namespace: "systemui"
description: "Build time flag for 2025 icon refresh"
@@ -1994,13 +2004,6 @@ flag {
}
flag {
- name: "notification_magic_actions_treatment"
- namespace: "systemui"
- description: "Special UI treatment for magic actions"
- bug: "383567383"
-}
-
-flag {
name: "notification_animated_actions_treatment"
namespace: "systemui"
description: "Special UI treatment for animated actions and replys"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 440a81fc2152..0680faf5226a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -1483,7 +1483,8 @@ constructor(
// TODO(b/397646693): remove this exception.
val isEligibleForReparenting = controller.isLaunching
val viewRoot = controller.transitionContainer.viewRootImpl
- val skipReparenting = skipReparentTransaction || viewRoot == null
+ val skipReparenting =
+ skipReparentTransaction || !window.leash.isValid || viewRoot == null
if (moveTransitionAnimationLayer() && isEligibleForReparenting && !skipReparenting) {
reparent = true
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 0181928317e1..1a0fb0afd385 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.ui.compose
+import android.content.res.Configuration
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -23,6 +24,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
@@ -66,6 +68,7 @@ constructor(
@Composable
fun ContentScope.Content(modifier: Modifier = Modifier) {
CommunalTouchableSurface(viewModel = viewModel, modifier = modifier) {
+ val orientation = LocalConfiguration.current.orientation
Layout(
modifier = Modifier.fillMaxSize(),
content = {
@@ -150,13 +153,29 @@ constructor(
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
+ val communalGridMaxHeight: Int
+ val communalGridPositionY: Int
+ if (Flags.communalResponsiveGrid()) {
+ val communalGridVerticalMargin = constraints.maxHeight - lockIconBounds.top
+ // Bias the widgets up by a small offset for visual balance in landscape
+ // orientation
+ val verticalOffset =
+ (if (orientation == Configuration.ORIENTATION_LANDSCAPE) (-3).dp else 0.dp)
+ .roundToPx()
+ // Use even top and bottom margin for grid to be centered in maxHeight (window)
+ communalGridMaxHeight = constraints.maxHeight - communalGridVerticalMargin * 2
+ communalGridPositionY = communalGridVerticalMargin + verticalOffset
+ } else {
+ communalGridMaxHeight = lockIconBounds.top
+ communalGridPositionY = 0
+ }
val communalGridPlaceable =
communalGridMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ noMinConstraints.copy(maxHeight = communalGridMaxHeight)
)
layout(constraints.maxWidth, constraints.maxHeight) {
- communalGridPlaceable.place(x = 0, y = 0)
+ communalGridPlaceable.place(x = 0, y = communalGridPositionY)
lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top)
val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
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 835dd7aa9f24..ad2a32e030bb 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
@@ -54,7 +54,10 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -76,8 +79,8 @@ import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.TextAutoSize
import androidx.compose.foundation.text.BasicText
+import androidx.compose.foundation.text.TextAutoSize
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@@ -99,6 +102,8 @@ import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@@ -174,6 +179,7 @@ import com.android.compose.animation.Easings.Emphasized
import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.internal.R.dimen.system_app_widget_background_radius
import com.android.systemui.Flags
import com.android.systemui.Flags.communalResponsiveGrid
@@ -254,6 +260,7 @@ fun CommunalHub(
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
val screenWidth = windowMetrics.bounds.width()
val layoutDirection = LocalLayoutDirection.current
+
if (viewModel.isEditMode) {
ObserveNewWidgetAddedEffect(communalContent, gridState, viewModel)
} else {
@@ -757,11 +764,33 @@ fun calculateWidgetSize(
}
@Composable
+private fun horizontalPaddingWithInsets(padding: Dp): Dp {
+ val orientation = LocalConfiguration.current.orientation
+ val displayCutoutPaddings = WindowInsets.displayCutout.asPaddingValues()
+ val horizontalDisplayCutoutPadding =
+ remember(orientation, displayCutoutPaddings) {
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ maxOf(
+ // Top in portrait becomes startPadding (or endPadding) in landscape
+ displayCutoutPaddings.calculateTopPadding(),
+ // Bottom in portrait becomes endPadding (or startPadding) in landscape
+ displayCutoutPaddings.calculateBottomPadding(),
+ )
+ } else {
+ 0.dp
+ }
+ }
+ return padding + horizontalDisplayCutoutPadding
+}
+
+@Composable
private fun HorizontalGridWrapper(
minContentPadding: PaddingValues,
gridState: LazyGridState,
dragDropState: GridDragDropState?,
setContentOffset: (offset: Offset) -> Unit,
+ minHorizontalArrangement: Dp,
+ minVerticalArrangement: Dp,
modifier: Modifier = Modifier,
content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit,
) {
@@ -775,8 +804,8 @@ private fun HorizontalGridWrapper(
state = gridState,
flingBehavior = flingBehavior,
minContentPadding = minContentPadding,
- minHorizontalArrangement = Dimensions.ItemSpacing,
- minVerticalArrangement = Dimensions.ItemSpacing,
+ minHorizontalArrangement = minHorizontalArrangement,
+ minVerticalArrangement = minVerticalArrangement,
setContentOffset = setContentOffset,
// Temporarily disable user gesture scrolling while dragging a widget to prevent
// conflicts between the drag and scroll gestures. Programmatic scrolling remains
@@ -833,6 +862,7 @@ private fun BoxScope.CommunalHubLazyGrid(
Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
var list = communalContent
var dragDropState: GridDragDropState? = null
+ var arrangementSpacing = Dimensions.ItemSpacing
if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
list = contentListState.list
// for drag & drop operations within the communal hub grid
@@ -866,6 +896,9 @@ private fun BoxScope.CommunalHubLazyGrid(
Box(Modifier.fillMaxSize().dragAndDropTarget(dragAndDropTargetState)) {}
} else if (communalResponsiveGrid()) {
gridModifier = gridModifier.fillMaxSize()
+ if (isCompactWindow()) {
+ arrangementSpacing = Dimensions.ItemSpacingCompact
+ }
} else {
gridModifier = gridModifier.height(hubDimensions.GridHeight)
}
@@ -875,6 +908,8 @@ private fun BoxScope.CommunalHubLazyGrid(
gridState = gridState,
dragDropState = dragDropState,
minContentPadding = minContentPadding,
+ minHorizontalArrangement = arrangementSpacing,
+ minVerticalArrangement = arrangementSpacing,
setContentOffset = setContentOffset,
) { sizeInfo ->
/** Override spans based on the responsive grid size */
@@ -1839,11 +1874,21 @@ private fun nonScalableTextSize(sizeInDp: Dp) = with(LocalDensity.current) { siz
@Composable
private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
if (!isEditMode || toolbarSize == null) {
- return PaddingValues(
- start = Dimensions.ItemSpacing,
- end = Dimensions.ItemSpacing,
- top = hubDimensions.GridTopSpacing,
- )
+ return if (communalResponsiveGrid()) {
+ val horizontalPaddings: Dp =
+ if (isCompactWindow()) {
+ horizontalPaddingWithInsets(Dimensions.ItemSpacingCompact)
+ } else {
+ Dimensions.ItemSpacing
+ }
+ PaddingValues(start = horizontalPaddings, end = horizontalPaddings)
+ } else {
+ PaddingValues(
+ start = Dimensions.ItemSpacing,
+ end = Dimensions.ItemSpacing,
+ top = hubDimensions.GridTopSpacing,
+ )
+ }
}
val context = LocalContext.current
val density = LocalDensity.current
@@ -1870,6 +1915,16 @@ private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): Padd
}
}
+/** Compact size in landscape or portrait */
+@Composable
+fun isCompactWindow(): Boolean {
+ val windowSizeClass = LocalWindowSizeClass.current
+ return remember(windowSizeClass) {
+ windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact ||
+ windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact
+ }
+}
+
private fun CommunalContentSize.FixedSize.dp(): Dp {
return when (this) {
CommunalContentSize.FixedSize.FULL -> Dimensions.CardHeightFull
@@ -1911,6 +1966,9 @@ class Dimensions(val context: Context, val config: Configuration) {
val CardHeightFull
get() = 530.adjustedDp
+ val ItemSpacingCompact
+ get() = 12.adjustedDp
+
val ItemSpacing
get() = if (communalResponsiveGrid()) 32.adjustedDp else 50.adjustedDp
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 6d4fffdefb1b..00710dc037fa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.scene.domain.startable
import android.app.StatusBarManager
@@ -121,6 +123,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -2608,6 +2611,75 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
+ fun handleDeviceUnlockStatus_returnsToLsFromBouncer_whenGoesToSleep() =
+ testScope.runTest {
+ val authMethod by collectLastValue(kosmos.authenticationInteractor.authenticationMethod)
+ val isUnlocked by
+ collectLastValue(
+ kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+ )
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ val isAwake by collectLastValue(powerInteractor.isAwake)
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ startsAwake = true,
+ )
+ underTest.start()
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).doesNotContain(Overlays.Bouncer)
+ assertThat(isAwake).isTrue()
+
+ sceneInteractor.showOverlay(Overlays.Bouncer, "")
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).contains(Overlays.Bouncer)
+ assertThat(isAwake).isTrue()
+
+ powerInteractor.setAsleepForTest()
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).doesNotContain(Overlays.Bouncer)
+ assertThat(isAwake).isFalse()
+ }
+
+ @Test
+ fun hidesBouncer_whenAuthMethodChangesToNonSecure() =
+ testScope.runTest {
+ val authMethod by collectLastValue(kosmos.authenticationInteractor.authenticationMethod)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(kosmos.sceneInteractor.currentOverlays)
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Password,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+ underTest.start()
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).doesNotContain(Overlays.Bouncer)
+
+ sceneInteractor.showOverlay(Overlays.Bouncer, "")
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).contains(Overlays.Bouncer)
+
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.None
+ )
+ runCurrent()
+
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).doesNotContain(Overlays.Bouncer)
+ }
+
+ @Test
fun replacesLockscreenSceneOnBackStack_whenFaceUnlocked_fromShade_noAlternateBouncer() =
testScope.runTest {
val transitionState =
@@ -2898,7 +2970,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
sceneInteractor.changeScene(it, "prepareState, initialSceneKey isn't null")
}
for (overlay in initialOverlays) {
- sceneInteractor.showOverlay(overlay, "prepareState, initialOverlays isn't empty")
+ sceneInteractor.instantlyShowOverlay(
+ overlay,
+ "prepareState, initialOverlays isn't empty",
+ )
}
if (startsAwake) {
powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index b376558371f3..0289c58f6e93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.disableDualShade
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.CommandQueue
@@ -214,6 +215,21 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
assertThat(currentOverlays).isEmpty()
}
+ @Test
+ fun instantCollapseShade_singleShade_doesntSwitchToShadeScene() =
+ testScope.runTest {
+ kosmos.disableDualShade()
+ runCurrent()
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val homeScene = currentScene
+ sceneInteractor.changeScene(Scenes.QuickSettings, "")
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+
+ underTest.instantCollapseShade()
+
+ assertThat(currentScene).isEqualTo(homeScene)
+ }
+
private fun setScene(key: SceneKey) {
sceneInteractor.changeScene(key, "test")
sceneInteractor.setTransitionState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 039a32ba9127..b4c6b33463b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -48,6 +48,7 @@ import com.android.wm.shell.appzoomout.AppZoomOut
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.function.Consumer
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -120,6 +121,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
`when`(blurUtils.supportsBlursOnWindows()).thenReturn(true)
`when`(blurUtils.maxBlurRadius).thenReturn(maxBlur.toFloat())
`when`(blurUtils.maxBlurRadius).thenReturn(maxBlur.toFloat())
+ `when`(windowRootViewBlurInteractor.isBlurCurrentlySupported)
+ .thenReturn(MutableStateFlow(true))
notificationShadeDepthController =
NotificationShadeDepthController(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
index 52f68bf4d729..2bb17e110974 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
@@ -37,7 +37,10 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
+@EnableFlags(NotificationBundleUi.FLAG_NAME)
class BundleEntryAdapterTest : SysuiTestCase() {
+ private lateinit var entry: BundleEntry
+
private val kosmos = testKosmos()
private lateinit var underTest: BundleEntryAdapter
@@ -46,120 +49,107 @@ class BundleEntryAdapterTest : SysuiTestCase() {
@Before
fun setUp() {
- underTest = factory.create(BundleEntry("key")) as BundleEntryAdapter
+ entry = BundleEntry("key")
+ underTest = factory.create(entry) as BundleEntryAdapter
+ }
+
+ @Test
+ fun getBackingHashCode() {
+ assertThat(underTest.backingHashCode).isEqualTo(entry.hashCode())
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getParent_adapter() {
assertThat(underTest.parent).isEqualTo(GroupEntry.ROOT_ENTRY)
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isTopLevelEntry_adapter() {
assertThat(underTest.isTopLevelEntry).isTrue()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getRow_adapter() {
assertThat(underTest.row).isNull()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupRoot_adapter() {
assertThat(underTest.isGroupRoot).isTrue()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getKey_adapter() {
assertThat(underTest.key).isEqualTo("key")
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isClearable_adapter() {
assertThat(underTest.isClearable).isTrue()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSummarization_adapter() {
assertThat(underTest.summarization).isNull()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getContrastedColor_adapter() {
assertThat(underTest.getContrastedColor(context, false, Color.WHITE))
.isEqualTo(Notification.COLOR_DEFAULT)
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canPeek_adapter() {
assertThat(underTest.canPeek()).isFalse()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getWhen_adapter() {
assertThat(underTest.`when`).isEqualTo(0)
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isColorized() {
assertThat(underTest.isColorized).isFalse()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSbn() {
assertThat(underTest.sbn).isNull()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canDragAndDrop() {
assertThat(underTest.canDragAndDrop()).isFalse()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isBubble() {
assertThat(underTest.isBubble).isFalse()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getStyle() {
assertThat(underTest.style).isNull()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSectionBucket() {
assertThat(underTest.sectionBucket).isEqualTo(underTest.entry.bucket)
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isAmbient() {
assertThat(underTest.isAmbient).isFalse()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canShowFullScreen() {
assertThat(underTest.isFullScreenCapable()).isFalse()
}
@Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getPeopleNotificationType() {
assertThat(underTest.getPeopleNotificationType()).isEqualTo(TYPE_NON_PERSON)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 4c18025c4cb7..2869979a230f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1804,7 +1804,8 @@ public class NotifCollectionTest extends SysuiTestCase {
}
private static EntryWithDismissStats entryWithDefaultStats(NotificationEntry entry) {
- return new EntryWithDismissStats(entry, defaultStats(entry));
+ return new EntryWithDismissStats(
+ entry, defaultStats(entry), entry.getKey(), entry.hashCode());
}
private CollectionEvent postNotif(NotificationEntryBuilder builder) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
index 02c6a484bd43..25b5d68cfbfa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -64,6 +64,17 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getBackingHashCode() {
+ val entry =
+ NotificationEntryBuilder()
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.backingHashCode).isEqualTo(entry.hashCode())
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getParent_adapter() {
val ge = GroupEntryBuilder().build()
val notification: Notification =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index f9405af3f85d..340ce673e01e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
@@ -36,7 +37,6 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.platform.test.annotations.EnableFlags;
import androidx.annotation.Nullable;
@@ -265,18 +265,35 @@ public class RankingCoordinatorTest extends SysuiTestCase {
}
@Test
- public void testIncludeInSectionSilent() {
- // GIVEN the entry isn't high priority
+ public void testSilentSectioner_accepts_highPriorityFalse_ambientFalse() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
setRankingAmbient(false);
+ assertOnlyInSection(mEntry, mSilentSectioner);
+ }
+
+ @Test
+ public void testSilentSectioner_rejects_highPriorityFalse_ambientTrue() {
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ setRankingAmbient(true);
+ assertFalse(mSilentSectioner.isInSection(mEntry));
+ }
+
+ @Test
+ public void testSilentSectioner_rejects_highPriorityTrue_ambientFalse() {
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
+ setRankingAmbient(false);
+ assertFalse(mSilentSectioner.isInSection(mEntry));
+ }
- // THEN entry is in the silent section
- assertFalse(mAlertingSectioner.isInSection(mEntry));
- assertTrue(mSilentSectioner.isInSection(mEntry));
+ @Test
+ public void testSilentSectioner_rejects_highPriorityTrue_ambientTrue() {
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
+ setRankingAmbient(true);
+ assertFalse(mSilentSectioner.isInSection(mEntry));
}
@Test
- public void testSilentSectioner_acceptsBundle() {
+ public void testSilentSectioner_accepts_bundle() {
BundleEntry bundleEntry = new BundleEntry("testBundleKey");
assertTrue(mSilentSectioner.isInSection(bundleEntry));
}
@@ -291,14 +308,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
public void testMinSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
setRankingAmbient(true);
- assertInSection(mEntry, mMinimizedSectioner);
- }
-
- @Test
- public void testSilentSection() {
- when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- setRankingAmbient(false);
- assertInSection(mEntry, mSilentSectioner);
+ assertOnlyInSection(mEntry, mMinimizedSectioner);
}
@Test
@@ -344,7 +354,8 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Test
public void testAlertingSectioner_rejectsBundle() {
for (String id : SYSTEM_RESERVED_IDS) {
- assertFalse(mAlertingSectioner.isInSection(makeClassifiedNotifEntry(id)));
+ assertFalse(
+ mAlertingSectioner.isInSection(makeClassifiedNotifEntry(id, IMPORTANCE_LOW)));
}
}
@@ -369,7 +380,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
reset(mInvalidationListener);
}
- private void assertInSection(NotificationEntry entry, NotifSectioner section) {
+ private void assertOnlyInSection(NotificationEntry entry, NotifSectioner section) {
for (NotifSectioner current: mSections) {
if (current == section) {
assertTrue(current.isInSection(entry));
@@ -396,16 +407,17 @@ public class RankingCoordinatorTest extends SysuiTestCase {
private void setRankingAmbient(boolean ambient) {
mEntry.setRanking(new RankingBuilder(mEntry.getRanking())
.setImportance(ambient
- ? NotificationManager.IMPORTANCE_MIN
+ ? IMPORTANCE_MIN
: IMPORTANCE_DEFAULT)
.build());
assertEquals(ambient, mEntry.getRanking().isAmbient());
}
- private NotificationEntry makeClassifiedNotifEntry(String channelId) {
- NotificationChannel channel = new NotificationChannel(channelId, channelId, IMPORTANCE_LOW);
+ private NotificationEntry makeClassifiedNotifEntry(String channelId, int importance) {
+ NotificationChannel channel = new NotificationChannel(channelId, channelId, importance);
return new NotificationEntryBuilder()
- .updateRanking((rankingBuilder -> rankingBuilder.setChannel(channel)))
+ .updateRanking((rankingBuilder ->
+ rankingBuilder.setChannel(channel).setImportance(importance)))
.build();
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 716353945be2..999a78af0c68 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.service.dreams.Flags.FLAG_DREAMS_V2;
+
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -38,6 +40,7 @@ import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
import android.view.ViewRootImpl;
@@ -337,6 +340,38 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
+ public void onBiometricAuthenticated_whenFaceAndDreaming_dontDismissKeyguard() {
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+ when(mUpdateMonitor.isDreaming()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
+
+ verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_ONLY_WAKE);
+ }
+
+ @Test
+ @EnableFlags(FLAG_DREAMS_V2)
+ public void onBiometricAuthenticated_whenFaceOnBouncerAndDreaming_dismissKeyguard() {
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+ when(mUpdateMonitor.isDreaming()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
+
+ verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM);
+ }
+
+ @Test
public void onBiometricAuthenticated_onLockScreen() {
// GIVEN not dozing
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
diff --git a/packages/SystemUI/res/drawable/notification_menu_button_background.xml b/packages/SystemUI/res/drawable/notification_menu_button_background.xml
new file mode 100644
index 000000000000..a3014d9ea566
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_menu_button_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#202124"/>
+ <corners android:radius="@dimen/notification_corner_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/unpin_icon.xml b/packages/SystemUI/res/drawable/unpin_icon.xml
index 4e2e15893884..979e8d440b78 100644
--- a/packages/SystemUI/res/drawable/unpin_icon.xml
+++ b/packages/SystemUI/res/drawable/unpin_icon.xml
@@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:viewportWidth="960"
+ android:tint="?attr/colorControlNormal"
android:viewportHeight="960"
- android:tint="?attr/colorControlNormal">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M680,120L680,200L640,200L640,527L560,447L560,200L400,200L400,287L313,200L280,167L280,167L280,120L680,120ZM480,920L440,880L440,640L240,640L240,560L320,480L320,434L56,168L112,112L848,848L790,904L526,640L520,640L520,880L480,920ZM354,560L446,560L402,516L400,514L354,560ZM480,367L480,367L480,367L480,367ZM402,516L402,516L402,516L402,516Z"/>
-</vector>
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M680,120L680,200L640,200L640,527L313,200L280,167L280,167L280,120L680,120ZM480,920L440,880L440,640L240,640L240,560L320,480L320,434L56,168L112,112L848,848L790,904L526,640L520,640L520,880L480,920Z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 8b5aeaac424a..8aa6930a09f9 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Default"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/battery_level_padding_start"
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index d5f9d4cc0954..a81d90bd13fc 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -76,7 +76,7 @@
android:gravity="center_vertical"
android:ellipsize="marquee"
android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.StatusBar.Carrier"
+ android:textAppearance="@style/TextAppearance.StatusBar.Default"
android:textColor="?attr/wallpaperTextColorSecondary"
android:singleLine="true"
systemui:showMissingSim="true"
diff --git a/packages/SystemUI/res/layout/promoted_menu_item.xml b/packages/SystemUI/res/layout/promoted_menu_item.xml
new file mode 100644
index 000000000000..ec52189585af
--- /dev/null
+++ b/packages/SystemUI/res/layout/promoted_menu_item.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_height="match_parent"
+ android:layout_width="@dimen/notification_menu_item_width"
+ android:background="@drawable/notification_menu_button_background"
+ android:backgroundTint="@androidprv:color/materialColorPrimaryContainer"
+ android:padding="@dimen/notification_menu_button_padding">
+ <ImageView
+ android:id="@+id/promoted_menuitem_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:tint="@androidprv:color/materialColorPrimary"
+ android:src="@drawable/unpin_icon" />
+ <TextView
+ android:id="@+id/promoted_menuitem_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_inline_disable_promotion_button"
+ style="@style/TextAppearance.NotificationMenuButtonText"/>
+ <androidx.constraintlayout.helper.widget.Flow
+ android:id="@+id/flow3"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:flow_verticalStyle="packed"
+ app:flow_horizontalAlign="center"
+ app:flow_verticalAlign="center"
+ app:constraint_referenced_ids="promoted_menuitem_icon,promoted_menuitem_text"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/promoted_permission_guts.xml b/packages/SystemUI/res/layout/promoted_permission_guts.xml
index 50e5ae3c05ed..e962d3ad8679 100644
--- a/packages/SystemUI/res/layout/promoted_permission_guts.xml
+++ b/packages/SystemUI/res/layout/promoted_permission_guts.xml
@@ -54,7 +54,7 @@
android:textColor="@androidprv:color/materialColorOnSurface"
android:minWidth="@dimen/min_clickable_item_size"
android:minHeight="@dimen/min_clickable_item_size"
- style="@style/TextAppearance.NotificationInfo.Button" />
+ style="@style/TextAppearance.NotificationMenuButtonText" />
<TextView
android:id="@+id/undo"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index e4da4729ad0d..359a69ca1f94 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -96,7 +96,7 @@
android:layout_width="wrap_content"
android:layout_height="@dimen/status_bar_system_icons_height"
android:layout_gravity="center_vertical"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Default.Clock"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index bb99d581c0b0..f3f4e880c121 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -45,6 +45,6 @@
android:clipChildren="false"
android:paddingEnd="@dimen/status_bar_battery_end_padding"
android:visibility="gone"
- systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+ systemui:textAppearance="@style/TextAppearance.StatusBar.Default" />
</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4dfb8cdf7920..ca984881713b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -300,6 +300,9 @@
<!-- Side padding on the side of notifications -->
<dimen name="notification_side_paddings">16dp</dimen>
+ <!-- Width of inline notification menu item buttons -->
+ <dimen name="notification_menu_item_width">112dp</dimen>
+
<!-- Starting translateY offset of the HUN appear and disappear animations. Indicates
the amount by the view is positioned above the screen before the animation starts. -->
<dimen name="heads_up_appear_y_above_screen">32dp</dimen>
@@ -370,10 +373,12 @@
<dimen name="min_notification_layout_height">48dp</dimen>
<!-- Size of the space to place a notification menu item -->
- <dimen name="notification_menu_icon_size">64dp</dimen>
+ <dimen name="notification_menu_icon_size">120dp</dimen>
<!-- The space around a notification menu item -->
<dimen name="notification_menu_icon_padding">20dp</dimen>
+ <!-- The space around a notification menu button -->
+ <dimen name="notification_menu_button_padding">8dp</dimen>
<!-- scroll view the size of 3 channel rows -->
<dimen name="notification_blocker_channel_list_height">192dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 681bd53f1a40..2d40c32e29e9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2124,6 +2124,9 @@
<!-- [CHAR LIMIT=30] Text shown in button used to prevent app from showing Live Updates, for this notification and all future ones -->
<string name="notification_inline_disable_promotion">Don\'t show as pinned</string>
+ <!-- [CHAR LIMIT=30] Text shown in button used to prevent app from showing Live Updates, for this notification and all future ones -->
+ <string name="notification_inline_disable_promotion_button">Block Live Updates from this app</string>
+
<!-- Text accompanying the "Show live updates" switch explaining the purpose of the setting -->
<string name="live_notifications_title">Showing Live Updates</string>
@@ -4210,9 +4213,9 @@
</string>
- <!-- Content of the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] -->
+ <!-- Content of interstitial shown after user revokes app permission to post Live Updates. [CHAR LIMIT=NONE] -->
<string name="demote_explain_text">
- <xliff:g id="application" example= "Superfast Food Delivery">%1$s</xliff:g> will no longer show Live Updates here. You can change this any time in Settings.
+ <xliff:g id="application" example= "Superfast Food Delivery">%1$s</xliff:g> will no longer show Live Updates. You can change this any time in Settings.
</string>
<!-- Template that joins disabled message with the label for the voice over. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a479f1841ca4..0e1f99f28850 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -17,18 +17,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
+ <style name="TextAppearance.StatusBar.Default" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
<item name="android:fontFamily" android:featureFlag="!com.android.systemui.status_bar_font_updates">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:fontFamily" android:featureFlag="com.android.systemui.status_bar_font_updates">"variable-label-large-emphasized"</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
- <item name="android:fontFeatureSettings">tnum</item>
</style>
- <style name="TextAppearance.StatusBar.Carrier" parent="@*android:style/TextAppearance.StatusBar.Icon">
- <item name="android:textSize">@dimen/status_bar_clock_size</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">@color/status_bar_clock_color</item>
+ <style name="TextAppearance.StatusBar.Default.Clock">
+ <item name="android:fontFeatureSettings">tnum</item>
</style>
<style name="TextAppearance.StatusBar.UserChip" parent="@*android:style/TextAppearance.StatusBar.Icon">
@@ -818,6 +815,14 @@
<item name="android:minWidth">0dp</item>
</style>
+ <style name="TextAppearance.NotificationMenuButtonText">
+ <item name="android:textSize">@dimen/notification_importance_header_text</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+
<style name="TextAppearance.HeadsUpStatusBarText"
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index a107322423bb..c5cd39ccbc9f 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -28,6 +28,7 @@ import android.os.ParcelFileDescriptor
import android.os.UserHandle
import android.util.Log
import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
import com.android.systemui.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED
import com.android.systemui.communal.data.backup.CommunalBackupHelper
import com.android.systemui.communal.data.backup.CommunalBackupUtils
@@ -118,7 +119,9 @@ open class BackupHelper : BackupAgentHelper() {
}
private fun communalEnabled(): Boolean {
- return resources.getBoolean(R.bool.config_communalServiceEnabled)
+ return resources.getBoolean(R.bool.config_communalServiceEnabled) ||
+ (Flags.glanceableHubV2() &&
+ resources.getBoolean(com.android.internal.R.bool.config_glanceableHubEnabled))
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 5b65531cdd55..f81745704d2b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -157,6 +157,7 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
}
+ @UiThread
fun updateContentDescription(
elapsedTimeDescription: CharSequence,
durationDescription: CharSequence,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index f69985ee5364..9cf7356a0ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -399,7 +399,9 @@ public class MediaControlPanel {
}
private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
- mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+ mMainExecutor.execute(() -> {
+ mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index e87d5de56177..8c683e8f9749 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -236,10 +236,12 @@ constructor(
durationDescription: CharSequence,
) {
if (!SceneContainerFlag.isEnabled) return
- seekBarObserver.updateContentDescription(
- elapsedTimeDescription,
- durationDescription,
- )
+ mainExecutor.execute {
+ seekBarObserver.updateContentDescription(
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 699778f3b6f9..1a0af514cf87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -162,6 +162,7 @@ fun LargeTileContent(
colors = colors,
accessibilityUiState = accessibilityUiState,
isVisible = isVisible,
+ modifier = Modifier.weight(1f),
)
if (sideDrawable != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 06fc8610c97b..daaa2db54775 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -332,6 +332,7 @@ constructor(
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
handleBouncerImeVisibility()
+ handleBouncerHiding()
handleSimUnlock()
handleDeviceUnlockStatus()
handlePowerState()
@@ -352,6 +353,24 @@ constructor(
}
}
+ private fun handleBouncerHiding() {
+ applicationScope.launch {
+ repeatWhen(
+ condition =
+ authenticationInteractor
+ .get()
+ .authenticationMethod
+ .map { !it.isSecure }
+ .distinctUntilChanged()
+ ) {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.Bouncer,
+ loggingReason = "Authentication method changed to a non-secure one.",
+ )
+ }
+ }
+ }
+
private fun handleSimUnlock() {
applicationScope.launch {
simBouncerInteractor
@@ -434,6 +453,12 @@ constructor(
}
}
+ if (powerInteractor.detailedWakefulness.value.isAsleep()) {
+ // The logic below is for when the device becomes unlocked. That must be a
+ // no-op if the device is not awake.
+ return@mapNotNull null
+ }
+
if (
isOnPrimaryBouncer &&
deviceUnlockStatus.deviceUnlockSource == DeviceUnlockSource.TrustAgent
@@ -833,7 +858,7 @@ constructor(
}
.collect {
val loggingReason = "Falsing detected."
- switchToScene(Scenes.Lockscreen, loggingReason)
+ switchToScene(targetSceneKey = Scenes.Lockscreen, loggingReason = loggingReason)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index b211f0729318..82d361797f96 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -101,6 +101,7 @@ constructor(
shadeInteractor.collapseQuickSettingsShade(
loggingReason = "ShadeControllerSceneImpl.instantCollapseShade",
transitionKey = Instant,
+ bypassNotificationsShade = true,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index f844d1da1a8d..50d634f6ac54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -334,6 +334,14 @@ constructor(
private fun onBlurApplied(appliedBlurRadius: Int, zoomOutFromShadeRadius: Float) {
lastAppliedBlur = appliedBlurRadius
+ onZoomOutChanged(zoomOutFromShadeRadius)
+ listeners.forEach { it.onBlurRadiusChanged(appliedBlurRadius) }
+ notificationShadeWindowController.setBackgroundBlurRadius(appliedBlurRadius)
+ }
+
+ private fun onZoomOutChanged(zoomOutFromShadeRadius: Float) {
+ TrackTracer.instantForGroup("shade", "zoom_out", zoomOutFromShadeRadius)
+ Log.v(TAG, "onZoomOutChanged $zoomOutFromShadeRadius")
wallpaperController.setNotificationShadeZoom(zoomOutFromShadeRadius)
if (spatialModelAppPushback()) {
appZoomOutOptional.ifPresent { appZoomOut ->
@@ -341,12 +349,15 @@ constructor(
}
keyguardInteractor.setZoomOut(zoomOutFromShadeRadius)
}
- listeners.forEach {
- it.onBlurRadiusChanged(appliedBlurRadius)
- }
- notificationShadeWindowController.setBackgroundBlurRadius(appliedBlurRadius)
}
+ private val applyZoomOutForFrame =
+ Choreographer.FrameCallback {
+ updateScheduled = false
+ val (_, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
+ onZoomOutChanged(zoomOutFromShadeRadius)
+ }
+
/** Animate blurs when unlocking. */
private val keyguardStateCallback =
object : KeyguardStateController.Callback {
@@ -627,8 +638,17 @@ constructor(
val (blur, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
zoomOutCalculatedFromShadeRadius = zoomOutFromShadeRadius
if (Flags.bouncerUiRevamp() || Flags.glanceableHubBlurredBackground()) {
- updateScheduled =
- windowRootViewBlurInteractor.requestBlurForShade(blur, shouldBlurBeOpaque)
+ if (windowRootViewBlurInteractor.isBlurCurrentlySupported.value) {
+ updateScheduled =
+ windowRootViewBlurInteractor.requestBlurForShade(blur, shouldBlurBeOpaque)
+ return
+ }
+ // When blur is not supported, zoom out still needs to happen when scheduleUpdate
+ // is invoked and a separate frame callback has to be wired-up to support that.
+ if (!updateScheduled) {
+ updateScheduled = true
+ choreographer.postFrameCallback(applyZoomOutForFrame)
+ }
return
}
if (updateScheduled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 6d3c12d139db..0ebe194018cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -6,8 +6,8 @@ caitlinshk@google.com
evanlaird@google.com
pixel@google.com
-per-file *Biometrics* = set noparent
-per-file *Biometrics* = file:../keyguard/OWNERS
+per-file *Biometric* = set noparent
+per-file *Biometric* = file:../keyguard/OWNERS
per-file *Doze* = set noparent
per-file *Doze* = file:../keyguard/OWNERS
per-file *Keyboard* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 104c2b546200..167035b2d17d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.chips.ui.compose
+import android.annotation.IdRes
import android.content.res.ColorStateList
+import android.util.Log
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
@@ -234,10 +237,35 @@ private fun StatusBarIcon(
AndroidView(
modifier = modifier,
factory = { _ ->
- iconFactory.invoke()?.apply {
- layoutParams = ViewGroup.LayoutParams(iconSizePx, iconSizePx)
- } ?: throw IllegalStateException("Missing StatusBarIconView for $notificationKey")
+ // Use a wrapper frame layout so that we still return a view even if the icon is null
+ val wrapperFrameLayout = FrameLayout(context)
+
+ val icon = iconFactory.invoke()
+ if (icon == null) {
+ Log.e(TAG, "Missing StatusBarIconView for $notificationKey")
+ } else {
+ icon.apply {
+ id = CUSTOM_ICON_VIEW_ID
+ layoutParams = ViewGroup.LayoutParams(iconSizePx, iconSizePx)
+ }
+ // If needed, remove the icon from its old parent (views can only be attached
+ // to 1 parent at a time)
+ (icon.parent as? ViewGroup)?.apply {
+ this.removeView(icon)
+ this.removeTransientView(icon)
+ }
+ wrapperFrameLayout.addView(icon)
+ }
+
+ wrapperFrameLayout
+ },
+ update = { frameLayout ->
+ frameLayout.findViewById<StatusBarIconView>(CUSTOM_ICON_VIEW_ID)?.apply {
+ this.imageTintList = colorTintList
+ }
},
- update = { iconView -> iconView.imageTintList = colorTintList },
)
}
+
+private const val TAG = "OngoingActivityChip"
+@IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index 8fc6cbe7c9e7..e69de29bb2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection;
-
-import static android.app.NotificationChannel.NEWS_ID;
-import static android.app.NotificationChannel.PROMOTIONS_ID;
-import static android.app.NotificationChannel.RECS_ID;
-import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
-
-import android.app.Notification;
-import android.content.Context;
-import android.os.Build;
-import android.service.notification.StatusBarNotification;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.systemui.statusbar.notification.icon.IconPack;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-import kotlinx.coroutines.flow.StateFlowKt;
-
-/**
- * Class to represent notifications bundled by classification.
- */
-public class BundleEntry extends PipelineEntry {
-
- // TODO(b/394483200): move NotificationEntry's implementation to PipelineEntry?
- private final MutableStateFlow<Boolean> mSensitive = StateFlowKt.MutableStateFlow(false);
-
- // TODO (b/389839319): implement the row
- private ExpandableNotificationRow mRow;
-
- private final List<ListEntry> mChildren = new ArrayList<>();
-
- private final List<ListEntry> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
-
- public BundleEntry(String key) {
- super(key);
- }
-
- void addChild(ListEntry child) {
- mChildren.add(child);
- }
-
- @NonNull
- public List<ListEntry> getChildren() {
- return mUnmodifiableChildren;
- }
-
- void clearChildren() {
- mChildren.clear();
- }
-
- /**
- * @return Null because bundles do not have an associated NotificationEntry.
- */
- @Nullable
- @Override
- public NotificationEntry getRepresentativeEntry() {
- return null;
- }
-
- @Nullable
- @Override
- public PipelineEntry getParent() {
- return null;
- }
-
- @Override
- public boolean wasAttachedInPreviousPass() {
- return false;
- }
-
- @Nullable
- public ExpandableNotificationRow getRow() {
- return mRow;
- }
-
- public static final List<BundleEntry> ROOT_BUNDLES = List.of(
- new BundleEntry(PROMOTIONS_ID),
- new BundleEntry(SOCIAL_MEDIA_ID),
- new BundleEntry(NEWS_ID),
- new BundleEntry(RECS_ID));
-
- public MutableStateFlow<Boolean> isSensitive() {
- return mSensitive;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt
new file mode 100644
index 000000000000..0da76c333a1f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.NotificationChannel
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import java.util.Collections
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Class to represent notifications bundled by classification. */
+class BundleEntry(key: String) : PipelineEntry(key) {
+ // TODO(b/394483200): move NotificationEntry's implementation to PipelineEntry?
+ val isSensitive: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ // TODO (b/389839319): implement the row
+ val row: ExpandableNotificationRow? = null
+
+ private val _children: MutableList<ListEntry> = ArrayList()
+ val children: List<ListEntry> = Collections.unmodifiableList(_children)
+
+ fun addChild(child: ListEntry) {
+ _children.add(child)
+ }
+
+ fun clearChildren() {
+ _children.clear()
+ }
+
+ /** @return Null because bundles do not have an associated NotificationEntry. */
+ override fun getRepresentativeEntry(): NotificationEntry? {
+ return null
+ }
+
+ override fun getParent(): PipelineEntry? {
+ return null
+ }
+
+ override fun wasAttachedInPreviousPass(): Boolean {
+ return false
+ }
+
+ companion object {
+ val ROOT_BUNDLES: List<BundleEntry> =
+ listOf(
+ BundleEntry(NotificationChannel.PROMOTIONS_ID),
+ BundleEntry(NotificationChannel.SOCIAL_MEDIA_ID),
+ BundleEntry(NotificationChannel.NEWS_ID),
+ BundleEntry(NotificationChannel.RECS_ID),
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 6a3f8f166c34..98714aeb1248 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -33,6 +33,11 @@ class BundleEntryAdapter(
private val highPriorityProvider: HighPriorityProvider,
val entry: BundleEntry,
) : EntryAdapter {
+
+ override fun getBackingHashCode(): Int {
+ return entry.hashCode()
+ }
+
/** TODO (b/394483200): convert to PipelineEntry.ROOT_ENTRY when pipeline is migrated? */
override fun getParent(): GroupEntry {
return GroupEntry.ROOT_ENTRY
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 16d9c787d435..43ae4d9296c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -36,6 +36,11 @@ import kotlinx.coroutines.flow.StateFlow;
public interface EntryAdapter {
/**
+ * Returns the hash code of the backing entry
+ */
+ int getBackingHashCode();
+
+ /**
* Gets the parent of this entry, or null if the entry's view is not attached
*/
@Nullable PipelineEntry getParent();
@@ -195,5 +200,6 @@ public interface EntryAdapter {
NotificationEntry.DismissState getDismissState();
void onEntryClicked(ExpandableNotificationRow row);
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
index 48a8c01e7c47..e37a210c1c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
@@ -23,7 +23,10 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
* A holder class for a [NotificationEntry] and an associated [DismissedByUserStats], used by
* [NotifCollection] for handling dismissal.
*/
-data class EntryWithDismissStats(val entry: NotificationEntry, val stats: DismissedByUserStats) {
+data class EntryWithDismissStats(val entry: NotificationEntry?,
+ val stats: DismissedByUserStats,
+ val key: String,
+ val entryHashCode: Int) {
/**
* Creates deep a copy of this object, but with the entry, key and rank updated to correspond to
* the given entry.
@@ -42,5 +45,7 @@ data class EntryWithDismissStats(val entry: NotificationEntry, val stats: Dismis
/* visible= */ false,
),
),
+ key = newEntry.key,
+ entryHashCode = newEntry.hashCode()
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b7fe39e9c757..10d7b9cce559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -98,6 +98,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Ra
import com.android.systemui.statusbar.notification.collection.notifcollection.RankingUpdatedEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.UpdateSource;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.util.Assert;
import com.android.systemui.util.NamedListenerSet;
import com.android.systemui.util.time.SystemClock;
@@ -283,53 +284,55 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
final int entryCount = entriesToDismiss.size();
final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
for (int i = 0; i < entriesToDismiss.size(); i++) {
- NotificationEntry entry = entriesToDismiss.get(i).getEntry();
+ String key = entriesToDismiss.get(i).getKey();
+ int hashCode = entriesToDismiss.get(i).getEntryHashCode();
DismissedByUserStats stats = entriesToDismiss.get(i).getStats();
requireNonNull(stats);
- NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
+ NotificationEntry storedEntry = mNotificationSet.get(key);
if (storedEntry == null) {
- mLogger.logDismissNonExistentNotif(entry, i, entryCount);
+ mLogger.logDismissNonExistentNotif(key, i, entryCount);
continue;
}
- if (entry != storedEntry) {
+ if (hashCode != storedEntry.hashCode()) {
throw mEulogizer.record(
new IllegalStateException("Invalid entry: "
- + "different stored and dismissed entries for " + logKey(entry)
+ + "different stored and dismissed entries for " + logKey(key)
+ " (" + i + "/" + entryCount + ")"
- + " dismissed=@" + Integer.toHexString(entry.hashCode())
+ + " dismissed=@" + Integer.toHexString(hashCode)
+ " stored=@" + Integer.toHexString(storedEntry.hashCode())));
}
- if (entry.getDismissState() == DISMISSED) {
- mLogger.logDismissAlreadyDismissedNotif(entry, i, entryCount);
+ if (storedEntry.getDismissState() == DISMISSED) {
+ mLogger.logDismissAlreadyDismissedNotif(storedEntry, i, entryCount);
continue;
- } else if (entry.getDismissState() == PARENT_DISMISSED) {
- mLogger.logDismissAlreadyParentDismissedNotif(entry, i, entryCount);
+ } else if (storedEntry.getDismissState() == PARENT_DISMISSED) {
+ mLogger.logDismissAlreadyParentDismissedNotif(storedEntry, i, entryCount);
}
- updateDismissInterceptors(entry);
- if (isDismissIntercepted(entry)) {
- mLogger.logNotifDismissedIntercepted(entry, i, entryCount);
+ updateDismissInterceptors(storedEntry);
+ if (isDismissIntercepted(storedEntry)) {
+ mLogger.logNotifDismissedIntercepted(storedEntry, i, entryCount);
continue;
}
- entriesToLocallyDismiss.add(entry);
- if (!entry.isCanceled()) {
+ entriesToLocallyDismiss.add(storedEntry);
+ if (!storedEntry.isCanceled()) {
int finalI = i;
// send message to system server if this notification hasn't already been cancelled
mBgExecutor.execute(() -> {
try {
mStatusBarService.onNotificationClear(
- entry.getSbn().getPackageName(),
- entry.getSbn().getUser().getIdentifier(),
- entry.getSbn().getKey(),
+ storedEntry.getSbn().getPackageName(),
+ storedEntry.getSbn().getUser().getIdentifier(),
+ storedEntry.getSbn().getKey(),
stats.dismissalSurface,
stats.dismissalSentiment,
stats.notificationVisibility);
} catch (RemoteException e) {
// system process is dead if we're here.
- mLogger.logRemoteExceptionOnNotificationClear(entry, finalI, entryCount, e);
+ mLogger.logRemoteExceptionOnNotificationClear(
+ storedEntry, finalI, entryCount, e);
}
});
}
@@ -343,28 +346,43 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
List<EntryWithDismissStats> entriesToDismiss) {
final HashSet<NotificationEntry> entriesSet = new HashSet<>(entriesToDismiss.size());
for (EntryWithDismissStats entryToStats : entriesToDismiss) {
- entriesSet.add(entryToStats.getEntry());
+ NotificationEntry entry = getEntryFromDismissalStats(entryToStats);
+ if (entry != null) {
+ entriesSet.add(entry);
+ }
}
final List<EntryWithDismissStats> entriesPlusSummaries =
new ArrayList<>(entriesToDismiss.size() + 1);
for (EntryWithDismissStats entryToStats : entriesToDismiss) {
entriesPlusSummaries.add(entryToStats);
- NotificationEntry summary = fetchSummaryToDismiss(entryToStats.getEntry());
- if (summary != null && !entriesSet.contains(summary)) {
- entriesPlusSummaries.add(entryToStats.copyForEntry(summary));
+ NotificationEntry entry = getEntryFromDismissalStats(entryToStats);
+ if (entry != null) {
+ NotificationEntry summary = fetchSummaryToDismiss(entry);
+ if (summary != null && !entriesSet.contains(summary)) {
+ entriesPlusSummaries.add(entryToStats.copyForEntry(summary));
+ }
}
}
return entriesPlusSummaries;
}
+ private NotificationEntry getEntryFromDismissalStats(EntryWithDismissStats stats) {
+ if (NotificationBundleUi.isEnabled()) {
+ return mNotificationSet.get(stats.getKey());
+ } else {
+ return stats.getEntry();
+ }
+ }
+
/**
* Dismisses a single notification on behalf of the user.
*/
public void dismissNotification(
NotificationEntry entry,
@NonNull DismissedByUserStats stats) {
- dismissNotifications(List.of(new EntryWithDismissStats(entry, stats)));
+ dismissNotifications(List.of(new EntryWithDismissStats(
+ entry, stats, entry.getKey(), entry.hashCode())));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 339a999e1535..b8b4e9886c66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -44,6 +44,9 @@ class NotificationEntryAdapter(
private val headsUpManager: HeadsUpManager,
private val entry: NotificationEntry,
) : EntryAdapter {
+ override fun getBackingHashCode(): Int {
+ return entry.hashCode()
+ }
override fun getParent(): PipelineEntry? {
return entry.parent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index ae2c70a284e9..cfd42d5a5cae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -119,9 +119,9 @@ class NotifCollectionLogger @Inject constructor(
})
}
- fun logDismissNonExistentNotif(entry: NotificationEntry, index: Int, count: Int) {
+ fun logDismissNonExistentNotif(entryKey: String, index: Int, count: Int) {
buffer.log(TAG, INFO, {
- str1 = entry.logKey
+ str1 = logKey(entryKey)
int1 = index
int2 = count
}, {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index f494a4ce40dd..2e3a95e07083 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -39,6 +39,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
+import android.widget.ImageView;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
@@ -485,19 +486,23 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
@Override
public void onParentHeightUpdate() {
- if (mParent == null
- || (mLeftMenuItems.isEmpty() && mRightMenuItems.isEmpty())
- || mMenuContainer == null) {
- return;
- }
- int parentHeight = mParent.getActualHeight();
- float translationY;
- if (parentHeight < mVertSpaceForIcons) {
- translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
- } else {
- translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
+ // If we are using only icon-based buttons, adjust layout for height changes.
+ // For permission helper full-layout buttons, do not adjust.
+ if (!Flags.permissionHelperInlineUiRichOngoing()) {
+ if (mParent == null
+ || (mLeftMenuItems.isEmpty() && mRightMenuItems.isEmpty())
+ || mMenuContainer == null) {
+ return;
+ }
+ int parentHeight = mParent.getActualHeight();
+ float translationY;
+ if (parentHeight < mVertSpaceForIcons) {
+ translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
+ } else {
+ translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
+ }
+ mMenuContainer.setTranslationY(translationY);
}
- mMenuContainer.setTranslationY(translationY);
}
@Override
@@ -697,8 +702,11 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
PromotedPermissionGutsContent demoteContent =
(PromotedPermissionGutsContent) LayoutInflater.from(context).inflate(
R.layout.promoted_permission_guts, null, false);
+ View demoteButton = LayoutInflater.from(context)
+ .inflate(R.layout.promoted_menu_item, null, false);
MenuItem info = new NotificationMenuItem(context, null, demoteContent,
- R.drawable.unpin_icon);
+ demoteButton);
+
return info;
}
@@ -758,10 +766,12 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
menuView.setAlpha(mAlpha);
parent.addView(menuView);
menuView.setOnClickListener(this);
- FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
- lp.width = mHorizSpaceForIcon;
- lp.height = mHorizSpaceForIcon;
- menuView.setLayoutParams(lp);
+ if (item instanceof ImageView) {
+ FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
+ lp.width = mHorizSpaceForIcon;
+ lp.height = mHorizSpaceForIcon;
+ menuView.setLayoutParams(lp);
+ }
}
mMenuItemsByView.put(menuView, item);
}
@@ -860,6 +870,17 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mGutsContent = content;
}
+
+ /**
+ * Add a new 'guts' panel with custom view.
+ */
+ public NotificationMenuItem(Context context, String contentDescription, GutsContent content,
+ View itemView) {
+ mMenuView = itemView;
+ mContentDescription = contentDescription;
+ mGutsContent = content;
+ }
+
@Override
@Nullable
public View getMenuView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 66c9b17ef235..7ac7905c8a48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -97,6 +97,7 @@ import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -1654,6 +1655,13 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mVisibilityProvider.obtain(entry, true));
}
+ private DismissedByUserStats getDismissedByUserStats(String entryKey) {
+ return new DismissedByUserStats(
+ DISMISSAL_SHADE,
+ DISMISS_SENTIMENT_NEUTRAL,
+ mVisibilityProvider.obtain(entryKey, true));
+ }
+
private View getGutsView() {
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
@@ -1705,9 +1713,19 @@ public class NotificationStackScrollLayoutController implements Dumpable {
final List<EntryWithDismissStats>
entriesWithRowsDismissedFromShade = new ArrayList<>();
for (ExpandableNotificationRow row : viewsToRemove) {
- final NotificationEntry entry = row.getEntry();
- entriesWithRowsDismissedFromShade.add(
- new EntryWithDismissStats(entry, getDismissedByUserStats(entry)));
+ if (NotificationBundleUi.isEnabled()) {
+ EntryAdapter entryAdapter = row.getEntryAdapter();
+ entriesWithRowsDismissedFromShade.add(
+ new EntryWithDismissStats(null,
+ getDismissedByUserStats(entryAdapter.getKey()),
+ entryAdapter.getKey(),
+ entryAdapter.getBackingHashCode()));
+ } else {
+ final NotificationEntry entry = row.getEntryLegacy();
+ entriesWithRowsDismissedFromShade.add(
+ new EntryWithDismissStats(entry, getDismissedByUserStats(entry),
+ entry.getKey(), entry.hashCode()));
+ }
}
mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 8a5b22183563..8f1d59c62844 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
+import static android.service.dreams.Flags.dreamsV2;
import android.annotation.IntDef;
import android.content.res.Resources;
@@ -662,6 +663,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
final boolean deviceDreaming = mUpdateMonitor.isDreaming();
final boolean bypass = mKeyguardBypassController.getBypassEnabled()
|| mAuthController.isUdfpsFingerDown();
+ final boolean isBouncerShowing = mKeyguardViewController.primaryBouncerIsOrWillBeShowing()
+ || mKeyguardTransitionInteractor.getCurrentState()
+ == KeyguardState.ALTERNATE_BOUNCER;
logCalculateModeForPassiveAuth(unlockingAllowed, deviceInteractive, isKeyguardShowing,
deviceDreaming, bypass, isStrongBiometric);
@@ -685,15 +689,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
}
if (unlockingAllowed && deviceDreaming) {
- return bypass ? MODE_WAKE_AND_UNLOCK_FROM_DREAM : MODE_ONLY_WAKE;
+ final boolean wakeAndUnlock = bypass || (dreamsV2() && isBouncerShowing);
+ return wakeAndUnlock ? MODE_WAKE_AND_UNLOCK_FROM_DREAM : MODE_ONLY_WAKE;
}
if (unlockingAllowed && mKeyguardStateController.isOccluded()) {
return MODE_UNLOCK_COLLAPSING;
}
if (isKeyguardShowing) {
- if ((mKeyguardViewController.primaryBouncerIsOrWillBeShowing()
- || mKeyguardTransitionInteractor.getCurrentState()
- == KeyguardState.ALTERNATE_BOUNCER) && unlockingAllowed) {
+ if (isBouncerShowing && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed && bypass) {
return MODE_UNLOCK_COLLAPSING;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index fa4fe46e690c..83e5db4db6fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -459,7 +459,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
/** Should only be called from {@link KeyguardStatusBarViewController}. */
void onOverlayChanged() {
- final int carrierTheme = R.style.TextAppearance_StatusBar_Clock;
+ final int carrierTheme = R.style.TextAppearance_StatusBar_Default;
mCarrierLabel.setTextAppearance(carrierTheme);
if (mBatteryView != null) {
mBatteryView.updatePercentView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index 36d64a9b405e..bc3eb23f5d09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -34,6 +34,7 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.systemui.Flags;
import com.android.systemui.compose.ComposeInitializer;
import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
@@ -117,9 +118,15 @@ public class StatusBarWindowView extends FrameLayout {
* bound of the status bar view, in order for the touch event to be correctly dispatched down,
* we jot down the position Y of the initial touch down event, offset it to 0 in the y-axis,
* and calculate the movement based on first touch down position.
+ *
+ * TODO(b/391894499): Remove this doc once Flags.statusBarWindowNoCustomTouch() is rolled out.
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (Flags.statusBarWindowNoCustomTouch()) {
+ return super.dispatchTouchEvent(ev);
+ }
+
if (ev.getAction() == ACTION_DOWN && ev.getRawY() > getHeight()) {
mTouchDownY = ev.getRawY();
ev.setLocation(ev.getRawX(), mTopInset);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 574b2c010a37..fc33db6e57e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -445,27 +445,30 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Test
@EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
- fun onTouch_actionDown_propagatesToDisplayPolicy() {
+ fun onInterceptTouchEvent_actionDown_propagatesToDisplayPolicy() {
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- controller.onTouch(event)
+
+ view.onInterceptTouchEvent(event)
verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
}
@Test
@EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
- fun onTouch_actionUp_notPropagatesToDisplayPolicy() {
+ fun onInterceptTouchEvent_actionUp_notPropagatesToDisplayPolicy() {
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
- controller.onTouch(event)
+
+ view.onInterceptTouchEvent(event)
verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any(), any())
}
@Test
@DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
- fun onTouch_shadeWindowGoesAroundDisabled_notPropagatesToDisplayPolicy() {
+ fun onInterceptTouchEvent_shadeWindowGoesAroundDisabled_notPropagatesToDisplayPolicy() {
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- controller.onTouch(event)
+
+ view.onInterceptTouchEvent(event)
verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(eq(event), any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 4a0445d5543a..5b29bffc5148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -67,6 +67,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -409,9 +410,14 @@ public class RemoteInputViewTest extends SysuiTestCase {
// fast forward to end of animation
mAnimatorTestRule.advanceTimeBy(1);
- // assert that fadeOutView's alpha is reset to 1f after the animation (hidden behind
- // RemoteInputView)
- assertEquals(1f, fadeOutView.getAlpha());
+ if (Flags.notificationRowTransparency()) {
+ // With transparent rows, fadeOutView should be hidden after the animation.
+ assertEquals(0f, fadeOutView.getAlpha());
+ } else {
+ // assert that fadeOutView's alpha is reset to 1f after the animation (hidden behind
+ // RemoteInputView)
+ assertEquals(1f, fadeOutView.getAlpha());
+ }
assertFalse(view.isAnimatingAppearance());
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(1f, view.getAlpha());
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md
index 4b2f96804c97..142492eace98 100644
--- a/ravenwood/api-maintainers.md
+++ b/ravenwood/api-maintainers.md
@@ -60,8 +60,51 @@ public class MyStruct {
// doComplex$ravenwood() method implementation under Ravenwood
}
- public void doComplex$ravenwood() {
- // This method implementation only runs under Ravenwood
+ private void doComplex$ravenwood() {
+ // This method implementation only runs under Ravenwood.
+ // The visibility of this replacement method does not need to match
+ // the original method, so it's recommmended to always use
+ // private methods so that these methods won't be accidentally used
+ // by unexpected users.
+ }
+}
+```
+
+## Redirect a complex method to a separate class when under Ravenwood
+
+```
+@RavenwoodKeepPartialClass
+@RavenwoodRedirectionClass("MyComplexClass_ravenwood")
+public class MyComplexClass {
+ @RavenwoodRedirect
+ public void doComplex(int i, int j, int k) {
+ // This method implementation runs as-is on devices, but calls to this
+ // method is redirected to MyComplexClass_ravenwood#doComplex()
+ // under Ravenwood.
+ }
+
+ @RavenwoodRedirect
+ public static void staticDoComplex(int i, int j, int k) {
+ // This method implementation runs as-is on devices, but calls to this
+ // method is redirected to MyComplexClass_ravenwood#staticDoComplex()
+ // under Ravenwood.
+ }
+
+/**
+ * This class is only available in the Ravenwood environment.
+ */
+class MyComplexClass_ravenwood {
+ public static void doComplex(MyComplexClass obj, int i, int j, int k) {
+ // Because the original method is a non-static method, the current
+ // object instance of the original method (the "this" reference)
+ // will be passed over as the first argument of the redirection method,
+ // with the remaining arguments to follow.
+ }
+
+ public static void staticDoComplex(int i, int j, int k) {
+ // Because the original method is a static method, there is no current
+ // object instance, so the parameter list of the redirection method
+ // is the same as the original method.
}
}
```
@@ -74,21 +117,6 @@ For example, consider a constructor or static initializer that relies on unsuppo
## Strategies for JNI
-At the moment, JNI isn't yet supported under Ravenwood, but you may still want to support APIs that are partially implemented with JNI. The current approach is to use the “replace” strategy to offer a pure-Java alternative implementation for any JNI-provided logic.
+At the moment, JNI support is considered "experimental". To ensure that teams are well-supported, for any JNI usage, please reach out to ravenwood@ so we can offer design advice and help you onboard native methods. If a native method can be trivially re-implemented in pure-Java, using the replacement or redirection mechanisms described above is also a viable option.
-Since this approach requires potentially complex re-implementation, it should only be considered for core infrastructure that is critical to unblocking widespread testing use-cases. Other less-common usages of JNI should instead wait for offical JNI support in the Ravenwood environment.
-
-When a pure-Java implementation grows too large or complex to host within the original class, the `@RavenwoodNativeSubstitutionClass` annotation can be used to host it in a separate source file:
-
-```
-@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass("com.android.platform.test.ravenwood.nativesubstitution.MyComplexClass_host")
-public class MyComplexClass {
- private static native void nativeDoThing(long nativePtr);
-...
-
-public class MyComplexClass_host {
- public static void nativeDoThing(long nativePtr) {
- // ...
- }
-```
+The main caveat regarding JNI support on Ravenwood is due to how native code is built for Ravenwood. Unlike Java/Kotlin code where Ravenwood directly uses the same intermediate artifacts when building the real target for Android, Ravenwood uses the "host" variant of native libraries. The "host" variant of native libraries usually either don't exist, are extremely limited, or behave drastically different compared to the Android variant.
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 6d82a744bc4f..b1030ff4e7ed 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -17,7 +17,7 @@ To run all Ravenwood tests, use:
To run a specific test, use "atest" as normal, selecting the test from a Ravenwood suite such as:
```
-atest CtsOsTestCasesRavenwood:ParcelTest\#testSetDataCapacityNegative
+atest CtsOsTestCasesRavenwood:ParcelTest#testSetDataCapacityNegative
```
## Typical test structure
@@ -63,63 +63,63 @@ public class MyCodeTest {
}
```
-* APIs available under Ravenwood are stateless by default. If your test requires explicit states (such as defining the UID you’re running under, or requiring a main `Looper` thread), add a `RavenwoodRule` to declare that:
+Once you’ve defined your test, you can use typical commands to execute it locally:
```
-import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class MyCodeTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .setProcessApp()
- .setProvideMainThread(true)
- .build();
+$ atest MyTestsRavenwood
```
-Once you’ve defined your test, you can use typical commands to execute it locally:
+You can also run your new tests automatically via `TEST_MAPPING` rules like this:
```
-$ atest --host MyTestsRavenwood
+{
+ "ravenwood-presubmit": [
+ {
+ "name": "MyTestsRavenwood",
+ "host": true
+ }
+ ]
+}
```
-> **Note:** There's a known bug #312525698 where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until the bug is fixed.
+## Using resources
-You can also run your new tests automatically via `TEST_MAPPING` rules like this:
+At the moment, the `android_ravenwood_test` module type cannot directly build resources yet. In order to use resources in Ravenwood tests, you have to build the resource APK in a separate `android_app` module and "borrow" the resources and R classes:
```
-{
- "ravenwood-presubmit": [
- {
- "name": "MyTestsRavenwood",
- "host": true
- }
- ]
+android_app {
+ name: "MyTests-res",
+ resource_dirs: ["res"],
+ // This has to be set to false, or ".aapt.srcjar" will not be generated
+ use_resource_processor: false,
}
-```
-> **Note:** There's a known bug #308854804 where `TEST_MAPPING` is not being applied, so we're currently planning to run all Ravenwood tests unconditionally in presubmit for changes to `frameworks/base/` and `cts/` until there is a better path forward.
+android_ravenwood_test {
+ name: "MyTestsRavenwood",
+ srcs: [
+ "src/**/*.java",
+ // ...
+
+ // Include R.java from the resource APK
+ ":MyTests-res{.aapt.srcjar}",
+ ],
+ // Set the resource APK
+ resource_apk: "MyTests-res",
+ // ...
+}
+```
## Strategies for migration/bivalent tests
Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can be dual-compiled to run on both a real Android device and under a Ravenwood environment.
-In situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly “ignore” that test under Ravenwood, while continuing to validate that test on real devices. The annotation can be applied to either individual methods or to an entire test class. Please note that your test class must declare a `RavenwoodRule` for the annotation to take effect.
+In situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly skip that test under Ravenwood, while continuing to validate that test on real devices. The annotation can be applied to either individual methods or to an entire test class.
Test authors are encouraged to provide a `blockedBy` or `reason` argument to help future maintainers understand why a test is being ignored, and under what conditions it might be supported in the future.
```
@RunWith(AndroidJUnit4.class)
public class MyCodeTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
@Test
public void testSimple() {
// Simple test that runs on both devices and Ravenwood
@@ -128,26 +128,11 @@ public class MyCodeTest {
@Test
@DisabledOnRavenwood(blockedBy = PackageManager.class)
public void testComplex() {
- // Complex test that runs on devices, but is ignored under Ravenwood
+ // Complex test that runs on devices, but is disabled under Ravenwood
}
}
```
-At the moment, the `android.content.res.Resources` subsystem isn't yet supported under Ravenwood, but you may still want to dual-compile test suites that depend on references to resources. Below is a strategy for supporting dual-compiliation, where you can "borrow" the generated resource symbols from your traditional `android_test` target:
-
-```
-android_test {
- name: "MyTestsDevice",
- resource_dirs: ["res"],
-...
-
-android_ravenwood_test {
- name: "MyTestsRavenwood",
- srcs: [
- ":MyTestsDevice{.aapt.srcjar}",
-...
-```
-
## Strategies for unsupported APIs
As you write tests against Ravenwood, you’ll likely discover API dependencies that aren’t supported yet. Here’s a few strategies that can help you make progress:
@@ -187,7 +172,4 @@ $ less /tmp/atest_result/20231128_133105_h9al__79/log/i*/i*/isolated-java-logs*
Here are some common known issues and recommended workarounds:
-* Some code may unconditionally interact with unsupported APIs, such as via static initializers. One strategy is to shift the logic into `@Before` methods and make it conditional by testing `RavenwoodRule.isUnderRavenwood()`.
-* Some code may reference API symbols not yet present in the Ravenwood runtime, such as ART or ICU internals, or APIs from Mainline modules. One strategy is to refactor to avoid these internal dependencies, but Ravenwood aims to better support them soon.
- * This may also manifest as very odd behavior, such as test not being executed at all, tracked by bug #312517322
- * This may also manifest as an obscure Mockito error claiming “Mockito can only mock non-private & non-final classes”
+* Some code may unconditionally interact with unsupported APIs, such as via static initializers. One strategy is to shift the logic into `@Before` methods and make it conditional by testing `RavenwoodRule.isOnRavenwood()`.
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 7462cc2f384a..c035688a8c84 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -323,6 +323,10 @@ com.android.internal.graphics.cam.Frame
com.android.internal.graphics.cam.HctSolver
com.android.internal.graphics.ColorUtils
+com.android.internal.protolog.common.LogLevel
+com.android.internal.protolog.LogcatOnlyProtoLogImpl
+com.android.internal.protolog.ProtoLog
+
com.android.internal.util.BitUtils
com.android.internal.util.BitwiseInputStream
com.android.internal.util.BitwiseOutputStream
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 39c1fa73b7ce..b4c45aba0131 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1823,11 +1823,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void notifyQuickSettingsTilesChanged(
@UserIdInt int userId, @NonNull List<ComponentName> tileComponentNames) {
notifyQuickSettingsTilesChanged_enforcePermission();
- if (DEBUG) {
- Slog.d(LOG_TAG, TextUtils.formatSimple(
- "notifyQuickSettingsTilesChanged userId: %d, tileComponentNames: %s",
- userId, tileComponentNames));
- }
+
+ Slog.d(LOG_TAG, String.format(
+ "notifyQuickSettingsTilesChanged userId: %s, tileComponentNames: %s",
+ userId, tileComponentNames));
final Set<ComponentName> newTileComponentNames = new ArraySet<>(tileComponentNames);
final Set<ComponentName> addedTiles;
final Set<ComponentName> removedTiles;
@@ -2131,6 +2130,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
synchronized (mLock) {
if (mCurrentUserId == userId && mInitialized) {
+ Slog.w(LOG_TAG, String.format("userId: %d is already initialized", userId));
return;
}
@@ -3309,10 +3309,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @param forceUpdate whether to force an update of the app Clients.
*/
private void onUserStateChangedLocked(AccessibilityUserState userState, boolean forceUpdate) {
- if (DEBUG) {
- Slog.v(LOG_TAG, "onUserStateChangedLocked for user " + userState.mUserId + " with "
- + "forceUpdate: " + forceUpdate);
- }
+ Slog.v(LOG_TAG, String.format("onUserStateChangedLocked for userId: %d, forceUpdate: %s",
+ userState.mUserId, forceUpdate));
+
// TODO: Remove this hack
mInitialized = true;
updateLegacyCapabilitiesLocked(userState);
@@ -4361,6 +4360,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void enableShortcutForTargets(
boolean enable, @UserShortcutType int shortcutType,
@NonNull List<String> shortcutTargets, @UserIdInt int userId) {
+ Slog.d(LOG_TAG, String.format(
+ "enableShortcutForTargets: enable %s, shortcutType: %s, shortcutTargets: %s, "
+ + "userId: %s",
+ enable, shortcutType, shortcutTargets, userId));
if (shortcutType == UserShortcutType.GESTURE
&& !android.provider.Flags.a11yStandaloneGestureEnabled()) {
Slog.w(LOG_TAG,
@@ -4418,6 +4421,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
if (currentTargets.equals(validNewTargets)) {
+ Slog.d(LOG_TAG,
+ String.format(
+ "shortcutTargets are the same: skip modifying: target: %s, "
+ + "shortcutType: %s",
+ validNewTargets, shortcutType));
return;
}
persistColonDelimitedSetToSettingLocked(
@@ -4491,6 +4499,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void updateA11yTileServicesInQuickSettingsPanel(
Set<String> newQsTargets,
Set<String> currentQsTargets, @UserIdInt int userId) {
+ Slog.d(LOG_TAG,
+ String.format(
+ "updateA11yTileServicesInQuickSettingsPanel: newQsTargets: %s , "
+ + "currentQsTargets: %s, userId: %s",
+ newQsTargets, currentQsTargets, userId));
// Call StatusBarManager to add/remove tiles
final StatusBarManagerInternal statusBarManagerInternal =
LocalServices.getService(StatusBarManagerInternal.class);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index b39b5b1a7660..9b735d70abb5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -26,6 +26,7 @@ import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED_FOR_
import static android.view.autofill.AutofillManager.NO_SESSION;
import static android.view.autofill.AutofillManager.RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -761,20 +762,16 @@ final class AutofillManagerServiceImpl
return false;
}
- @GuardedBy("mLock")
- void removeSessionLocked(int sessionId) {
- mSessions.remove(sessionId);
- if (Flags.autofillSessionDestroyed()) {
- if (sVerbose) {
- Slog.v(
- TAG,
- "removeSessionLocked(): removed " + sessionId);
- }
+ void callOnSessionDestroyed(int sessionId) {
+ if (sVerbose) {
+ Slog.v(TAG, "removeSessionLocked(): removed " + sessionId);
+ }
+ synchronized (mLock) {
FillEventHistory history = null;
if (AutofillFeatureFlags.isMultipleFillEventHistoryEnabled()
- && mFillHistories != null) {
+ && mFillHistories != null) {
history = mFillHistories.get(sessionId);
mFillHistories.delete(sessionId);
}
@@ -806,6 +803,16 @@ final class AutofillManagerServiceImpl
}
}
+ @GuardedBy("mLock")
+ void removeSessionLocked(int sessionId) {
+ mSessions.remove(sessionId);
+ if (Flags.autofillSessionDestroyed()) {
+ mHandler.sendMessage(
+ obtainMessage(
+ AutofillManagerServiceImpl::callOnSessionDestroyed, this, sessionId));
+ }
+ }
+
/**
* Ges the previous sessions asked to be kept alive in a given activity task.
*
diff --git a/services/companion/java/com/android/server/companion/utils/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
index cfa7cb00dfac..91f7a3f23a1b 100644
--- a/services/companion/java/com/android/server/companion/utils/MetricUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
@@ -21,7 +21,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_VIRTUAL_DEVICE;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING;
@@ -33,8 +33,8 @@ import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_COMPUTER;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_GLASSES;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
-import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NULL;
+import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_VIRTUAL_DEVICE;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WATCH;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WEARABLE_SENSING;
import static com.android.internal.util.FrameworkStatsLog.write;
@@ -76,8 +76,8 @@ public final class MetricUtils {
CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NEARBY_DEVICE_STREAMING
);
map.put(
- DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
- CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING
+ DEVICE_PROFILE_VIRTUAL_DEVICE,
+ CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_VIRTUAL_DEVICE
);
map.put(
DEVICE_PROFILE_WEARABLE_SENSING,
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index f4128b820d8f..7157795d8998 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -28,7 +28,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_VIRTUAL_DEVICE;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -87,8 +87,8 @@ public final class PermissionsUtils {
map.put(DEVICE_PROFILE_GLASSES, Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES);
map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING);
- map.put(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
- Manifest.permission.REQUEST_COMPANION_PROFILE_SENSOR_DEVICE_STREAMING);
+ map.put(DEVICE_PROFILE_VIRTUAL_DEVICE,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE);
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index caf535ce7a40..b90f910cf759 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -105,7 +105,7 @@ public class VirtualDeviceManagerService extends SystemService {
AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
- AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
+ AssociationRequest.DEVICE_PROFILE_VIRTUAL_DEVICE);
/** Enable default device camera access for apps running on virtual devices. */
@ChangeId
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 03d6c8b695a0..71fa8cdfeb37 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -1697,14 +1697,12 @@ public class SystemConfig {
}
} break;
case "require-strict-signature": {
- if (android.security.Flags.extendVbChainToUpdatedApk()) {
- String packageName = parser.getAttributeValue(null, "package");
- if (TextUtils.isEmpty(packageName)) {
- Slog.w(TAG, "<" + name + "> without valid package in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mPreinstallPackagesWithStrictSignatureCheck.add(packageName);
- }
+ String packageName = parser.getAttributeValue(null, "package");
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.w(TAG, "<" + name + "> without valid package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mPreinstallPackagesWithStrictSignatureCheck.add(packageName);
}
} break;
case "oem-defined-uid": {
diff --git a/services/core/java/com/android/server/TradeInModeService.java b/services/core/java/com/android/server/TradeInModeService.java
index 1a9e02c86560..6cf9b7b33674 100644
--- a/services/core/java/com/android/server/TradeInModeService.java
+++ b/services/core/java/com/android/server/TradeInModeService.java
@@ -44,7 +44,6 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
-
public final class TradeInModeService extends SystemService {
private static final String TAG = "TradeInModeService";
@@ -129,7 +128,7 @@ public final class TradeInModeService extends SystemService {
@RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE)
public boolean start() {
mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE",
- "Cannot enter trade-in mode foyer");
+ "Cannot enter trade-in mode foyer");
final int state = getTradeInModeState();
if (state == TIM_STATE_FOYER) {
return true;
@@ -168,7 +167,7 @@ public final class TradeInModeService extends SystemService {
@RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE)
public boolean enterEvaluationMode() {
mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE",
- "Cannot enter trade-in evaluation mode");
+ "Cannot enter trade-in evaluation mode");
final int state = getTradeInModeState();
if (state != TIM_STATE_FOYER) {
Slog.e(TAG, "Cannot enter evaluation mode in state: " + state);
@@ -199,7 +198,7 @@ public final class TradeInModeService extends SystemService {
@RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE)
public boolean isEvaluationModeAllowed() {
mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE",
- "Cannot test for trade-in evaluation mode allowed");
+ "Cannot test for trade-in evaluation mode allowed");
return !isFrpActive();
}
@@ -246,7 +245,7 @@ public final class TradeInModeService extends SystemService {
private void enforceTestingPermissions() {
mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE",
- "Caller must have ENTER_TRADE_IN_MODE permission");
+ "Caller must have ENTER_TRADE_IN_MODE permission");
if (!isDebuggable()) {
throw new SecurityException("ro.debuggable must be set to 1");
}
@@ -302,7 +301,7 @@ public final class TradeInModeService extends SystemService {
private boolean scheduleTradeInModeWipe() {
try (FileWriter fw = new FileWriter(WIPE_INDICATOR_FILE,
- StandardCharsets.US_ASCII)) {
+ StandardCharsets.US_ASCII)) {
fw.write("0");
} catch (IOException e) {
Slog.e(TAG, "Failed to write " + WIPE_INDICATOR_FILE, e);
@@ -339,8 +338,7 @@ public final class TradeInModeService extends SystemService {
private boolean isFrpActive() {
try {
- PersistentDataBlockManager pdb =
- mContext.getSystemService(PersistentDataBlockManager.class);
+ PersistentDataBlockManager pdb = mContext.getSystemService(PersistentDataBlockManager.class);
if (pdb == null) {
return false;
}
@@ -351,9 +349,9 @@ public final class TradeInModeService extends SystemService {
}
}
- // This returns true if the device has progressed far enough into Setup Wizard that it no
- // longer makes sense to enable trade-in mode. As a last stop, we check the SUW completion
- // bits.
+ // This returns true if the device has progressed far enough into Setup Wizard
+ // that it no longer makes sense to enable trade-in mode. As a last stop, we
+ // check the SUW completion bits.
private boolean isDeviceSetup() {
final ContentResolver cr = mContext.getContentResolver();
try {
@@ -395,14 +393,13 @@ public final class TradeInModeService extends SystemService {
cr.registerContentObserver(deviceProvisioned, false, observer);
}
-
private void watchForNetworkChange() {
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
NetworkRequest networkRequest = new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .build();
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .build();
mNetworkCallback = new ConnectivityManager.NetworkCallback() {
@Override
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6b4a99cc4fae..d1225d4e6eed 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -3368,6 +3368,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
+ } else if ("--debug".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Missing time argument for --flags HEX");
+ dumpHelp(pw);
+ return;
+ }
+ flags |= ParseUtils.parseIntWithBase(args[i], 16, 0);
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
dumpHelp(pw);
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
index 700cf9c8deb8..99fdf9c8d229 100644
--- a/services/core/java/com/android/server/am/BroadcastHistory.java
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Trace;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -85,16 +86,26 @@ public class BroadcastHistory {
void onBroadcastFrozenLocked(@NonNull BroadcastRecord r) {
mFrozenBroadcasts.add(r);
+ updateTraceCounters();
}
void onBroadcastEnqueuedLocked(@NonNull BroadcastRecord r) {
mFrozenBroadcasts.remove(r);
mPendingBroadcasts.add(r);
+ updateTraceCounters();
}
void onBroadcastFinishedLocked(@NonNull BroadcastRecord r) {
mPendingBroadcasts.remove(r);
addBroadcastToHistoryLocked(r);
+ updateTraceCounters();
+ }
+
+ private void updateTraceCounters() {
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Broadcasts pending",
+ mPendingBroadcasts.size());
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Broadcasts frozen",
+ mFrozenBroadcasts.size());
}
public void addBroadcastToHistoryLocked(@NonNull BroadcastRecord original) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index ac30be99979e..e6bc36a3ed4c 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -668,12 +668,13 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
getBackgroundStartPrivilegesForActivitySender(
mAllowBgActivityStartsForBroadcastSender, allowlistToken,
options, callingUid);
+ final Bundle extras = createSafeActivityOptionsBundle(options);
// If a completion callback has been requested, require
// that the broadcast be delivered synchronously
int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
key.featureId, uid, callingUid, callingPid, finalIntent,
resolvedType, finishedReceiverThread, finishedReceiver, code, null,
- null, requiredPermission, options, (finishedReceiver != null),
+ extras, requiredPermission, options, (finishedReceiver != null),
false, userId, backgroundStartPrivileges,
null /* broadcastAllowList */);
if (sent == ActivityManager.BROADCAST_SUCCESS) {
@@ -716,6 +717,32 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
return res;
}
+ /**
+ * Creates a safe ActivityOptions bundle with only the launchDisplayId set.
+ *
+ * <p>This prevents unintended data from being sent to the app process. The resulting bundle
+ * is then used by {@link ActivityThread#createDisplayContextIfNeeded} to create a display
+ * context for the {@link BroadcastReceiver}, ensuring that activities launched from the
+ * receiver's context are started on the correct display.
+ *
+ * @param optionsBundle The original ActivityOptions bundle.
+ * @return A new bundle containing only the launchDisplayId from the original options, or null
+ * if the original bundle is null.
+ */
+ @Nullable
+ private Bundle createSafeActivityOptionsBundle(@Nullable Bundle optionsBundle) {
+ if (!com.android.window.flags.Flags.supportWidgetIntentsOnConnectedDisplay()) {
+ return null;
+ }
+ if (optionsBundle == null) {
+ return null;
+ }
+ final ActivityOptions options = ActivityOptions.fromBundle(optionsBundle);
+ return ActivityOptions.makeBasic()
+ .setLaunchDisplayId(options.getLaunchDisplayId())
+ .toBundle();
+ }
+
@VisibleForTesting BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
IBinder allowlistToken) {
return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
index 9c14b5b079b1..1b123e8663e8 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapper.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -28,15 +28,21 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.NoSuchElementException;
/**
- * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when necessary.
- * This is essentially a wrapper over IHealth that is useful for BatteryService.
+ * HealthServiceWrapper wraps the internal IHealth service and refreshes the
+ * service when necessary.
*
- * <p>The implementation may be backed by a HIDL or AIDL HAL.
+ * This is essentially a wrapper over IHealth that is useful for BatteryService,
+ * and TradeInModeService.
*
- * <p>On new registration of IHealth service, the internal service is refreshed. On death of an
- * existing IHealth service, the internal service is NOT cleared to avoid race condition between
- * death notification and new service notification. Hence, a caller must check for transaction
- * errors when calling into the service.
+ * <p>
+ * The implementation may be backed by a HIDL or AIDL HAL.
+ *
+ * <p>
+ * On new registration of IHealth service, the internal service is refreshed. On
+ * death of an existing IHealth service, the internal service is NOT cleared to
+ * avoid race condition between death notification and new service notification.
+ * Hence, a caller must check for transaction errors when calling into the
+ * service.
*
* @hide Should only be used internally.
*/
@@ -46,7 +52,8 @@ public abstract class HealthServiceWrapper {
abstract HandlerThread getHandlerThread();
/**
- * Calls into get*() functions in the health HAL. This reads into the kernel interfaces
+ * Calls into get*() functions in the health HAL. This reads into the kernel
+ * interfaces
* directly.
*
* @see IBatteryPropertiesRegistrar#getProperty
@@ -61,11 +68,14 @@ public abstract class HealthServiceWrapper {
public abstract void scheduleUpdate() throws RemoteException;
/**
- * Calls into getHealthInfo() in the health HAL. This returns a cached value in the health HAL
+ * Calls into getHealthInfo() in the health HAL. This returns a cached value in
+ * the health HAL
* implementation.
*
- * @return health info. {@code null} if no health HAL service. {@code null} if any
- * service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported.
+ * @return health info. {@code null} if no health HAL service. {@code null} if
+ * any
+ * service-specific error when calling {@code getHealthInfo}, e.g. it is
+ * unsupported.
* @throws RemoteException for any transaction-level errors
*/
public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException;
@@ -77,7 +87,7 @@ public abstract class HealthServiceWrapper {
* this one.
*
* @return battery health data. {@code null} if no health HAL service.
- * {@code null} if any service-specific error when calling {@code
+ * {@code null} if any service-specific error when calling {@code
* getBatteryHealthData}, e.g. it is unsupported.
* @throws RemoteException for any transaction-level errors
*/
@@ -89,31 +99,40 @@ public abstract class HealthServiceWrapper {
* Create a new HealthServiceWrapper instance.
*
* @param healthInfoCallback the callback to call when health info changes
- * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
- * @throws RemoteException transaction errors
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or
+ * AIDL service.
+ * @throws RemoteException transaction errors
* @throws NoSuchElementException no HIDL or AIDL service is available
*/
public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback)
throws RemoteException, NoSuchElementException {
return create(
healthInfoCallback == null ? null : new HealthRegCallbackAidl(healthInfoCallback),
- new HealthServiceWrapperAidl.ServiceManagerStub() {},
+ new HealthServiceWrapperAidl.ServiceManagerStub() {
+ },
healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback),
- new HealthServiceWrapperHidl.IServiceManagerSupplier() {},
- new HealthServiceWrapperHidl.IHealthSupplier() {});
+ new HealthServiceWrapperHidl.IServiceManagerSupplier() {
+ },
+ new HealthServiceWrapperHidl.IHealthSupplier() {
+ });
}
/**
* Create a new HealthServiceWrapper instance for testing.
*
- * @param aidlRegCallback callback for AIDL service registration, or {@code null} if the client
- * does not care about AIDL service registration notifications
- * @param aidlServiceManager Stub for AIDL ServiceManager
- * @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client
- * does not care about HIDL service registration notifications
+ * @param aidlRegCallback callback for AIDL service registration, or
+ * {@code null} if the client
+ * does not care about AIDL service
+ * registration notifications
+ * @param aidlServiceManager Stub for AIDL ServiceManager
+ * @param hidlRegCallback callback for HIDL service registration, or
+ * {@code null} if the client
+ * does not care about HIDL service
+ * registration notifications
* @param hidlServiceManagerSupplier supplier of HIDL service manager
- * @param hidlHealthSupplier supplier of HIDL health HAL
- * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ * @param hidlHealthSupplier supplier of HIDL health HAL
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or
+ * AIDL service.
*/
@VisibleForTesting
static @NonNull HealthServiceWrapper create(
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 3f75b11befc2..ea4b3d426346 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.location.flags.Flags;
import android.os.SystemClock;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -71,13 +72,25 @@ public class SystemEmergencyHelper extends EmergencyHelper {
return;
}
- synchronized (SystemEmergencyHelper.this) {
+ if (Flags.fixIsInEmergencyAnr()) {
try {
- mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
+ boolean isInEmergency = mTelephonyManager.isEmergencyNumber(
intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+ synchronized (SystemEmergencyHelper.this) {
+ mIsInEmergencyCall = isInEmergency;
+ }
} catch (IllegalStateException | UnsupportedOperationException e) {
Log.w(TAG, "Failed to call TelephonyManager.isEmergencyNumber().", e);
}
+ } else {
+ synchronized (SystemEmergencyHelper.this) {
+ try {
+ mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
+ intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+ } catch (IllegalStateException | UnsupportedOperationException e) {
+ Log.w(TAG, "Failed to call TelephonyManager.isEmergencyNumber().", e);
+ }
+ }
}
dispatchEmergencyStateChanged();
@@ -98,27 +111,55 @@ public class SystemEmergencyHelper extends EmergencyHelper {
}
@Override
- public synchronized boolean isInEmergency(long extensionTimeMs) {
- if (mTelephonyManager == null) {
- return false;
- }
+ public boolean isInEmergency(long extensionTimeMs) {
+ if (Flags.fixIsInEmergencyAnr()) {
+ if (mTelephonyManager == null) {
+ return false;
+ }
+ boolean emergencyCallbackMode = false;
+ boolean emergencySmsMode = false;
+ PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ }
+ boolean isInExtensionTime;
+ synchronized (this) {
+ isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
+ && (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs)
+ < extensionTimeMs;
+ return mIsInEmergencyCall
+ || isInExtensionTime
+ || emergencyCallbackMode
+ || emergencySmsMode;
+ }
+ } else {
+ synchronized (this) {
+ if (mTelephonyManager == null) {
+ return false;
+ }
- boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
- && (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs;
+ boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
+ && (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs)
+ < extensionTimeMs;
- boolean emergencyCallbackMode = false;
- boolean emergencySmsMode = false;
- PackageManager pm = mContext.getPackageManager();
- if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
- emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
- }
- if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
- emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ boolean emergencyCallbackMode = false;
+ boolean emergencySmsMode = false;
+ PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ }
+ return mIsInEmergencyCall
+ || isInExtensionTime
+ || emergencyCallbackMode
+ || emergencySmsMode;
+ }
}
- return mIsInEmergencyCall
- || isInExtensionTime
- || emergencyCallbackMode
- || emergencySmsMode;
}
private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 85dc811a7811..80cb5480fec1 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -168,6 +168,11 @@ public class KeySyncTask implements Runnable {
}
private void syncKeys() throws RemoteException {
+ if (mCredential != null && mCredential.length >= 80) {
+ // The value is likely a randomly generated profile password
+ // It doesn't match string typed by the user.
+ Log.e(TAG, "Unexpected credential length for user " + mUserId);
+ }
if (mCredentialUpdated && mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId) != 0) {
mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 0);
}
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 f4fe8e120ba1..0202b554b8aa 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -57,7 +57,9 @@ import android.media.quality.SoundProfileHandle;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.RemoteCallbackList;
@@ -116,6 +118,7 @@ public class MediaQualityService extends SystemService {
private HalNotifier mHalNotifier;
private MqManagerNotifier mMqManagerNotifier;
private MqDatabaseUtils mMqDatabaseUtils;
+ private Handler mHandler;
// A global lock for picture profile objects.
private final Object mPictureProfileLock = new Object();
@@ -141,6 +144,7 @@ public class MediaQualityService extends SystemService {
mHalNotifier = new HalNotifier();
mPictureProfileAdjListener = new PictureProfileAdjustmentListenerImpl();
mSoundProfileAdjListener = new SoundProfileAdjustmentListenerImpl();
+ mHandler = new Handler(Looper.getMainLooper());
// The package info in the context isn't initialized in the way it is for normal apps,
// so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
@@ -188,36 +192,39 @@ public class MediaQualityService extends SystemService {
@GuardedBy("mPictureProfileLock")
@Override
- public PictureProfile createPictureProfile(PictureProfile pp, int userId) {
- if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
- && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
- && !hasGlobalPictureQualityServicePermission()) {
- mMqManagerNotifier.notifyOnPictureProfileError(null,
- PictureProfile.ERROR_NO_PERMISSION,
- Binder.getCallingUid(), Binder.getCallingPid());
- }
-
- synchronized (mPictureProfileLock) {
- SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
-
- ContentValues values = MediaQualityUtils.getContentValues(null,
- pp.getProfileType(),
- pp.getName(),
- pp.getPackageName() == null || pp.getPackageName().isEmpty()
- ? getPackageOfCallingUid() : pp.getPackageName(),
- pp.getInputId(),
- pp.getParameters());
+ public void createPictureProfile(PictureProfile pp, int userId) {
+ mHandler.post(
+ () -> {
+ if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
+ && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
+ && !hasGlobalPictureQualityServicePermission()) {
+ mMqManagerNotifier.notifyOnPictureProfileError(null,
+ PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
- // id is auto-generated by SQLite upon successful insertion of row
- Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- null, values);
- MediaQualityUtils.populateTempIdMap(mPictureProfileTempIdMap, id);
- String value = mPictureProfileTempIdMap.getValue(id);
- pp.setProfileId(value);
- mMqManagerNotifier.notifyOnPictureProfileAdded(value, pp, Binder.getCallingUid(),
- Binder.getCallingPid());
- return pp;
- }
+ synchronized (mPictureProfileLock) {
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+
+ ContentValues values = MediaQualityUtils.getContentValues(null,
+ pp.getProfileType(),
+ pp.getName(),
+ pp.getPackageName() == null || pp.getPackageName().isEmpty()
+ ? getPackageOfCallingUid() : pp.getPackageName(),
+ pp.getInputId(),
+ pp.getParameters());
+
+ // id is auto-generated by SQLite upon successful insertion of row
+ Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+ null, values);
+ MediaQualityUtils.populateTempIdMap(mPictureProfileTempIdMap, id);
+ String value = mPictureProfileTempIdMap.getValue(id);
+ pp.setProfileId(value);
+ mMqManagerNotifier.notifyOnPictureProfileAdded(value, pp,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
+ }
+ );
}
@GuardedBy("mPictureProfileLock")
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c1e46a68b733..de20b82f505c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7410,6 +7410,10 @@ public class NotificationManagerService extends SystemService {
adjustments);
if (newChannel == null || newChannel.getId().equals(r.getChannel().getId())) {
adjustments.remove(KEY_TYPE);
+ } else if (android.app.Flags.apiRichOngoing() && hasFlag(r.getNotification().flags,
+ FLAG_PROMOTED_ONGOING)) {
+ // Don't bundle any promoted ongoing notifications
+ adjustments.remove(KEY_TYPE);
} else {
// Save the app-provided type for logging.
int classification = adjustments.getInt(KEY_TYPE);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index e02ec6a9e3b4..d98f3617f587 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4519,9 +4519,6 @@ final class InstallPackageHelper {
* at boot.
*/
private boolean needSignatureMatchToSystem(String packageName) {
- if (!android.security.Flags.extendVbChainToUpdatedApk()) {
- return false;
- }
return mPm.mInjector.getSystemConfig().getPreinstallPackagesWithStrictSignatureCheck()
.contains(packageName);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e00d80f860b0..f694dc924c02 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3610,10 +3610,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Needs to happen before the first v4 signature verification, which happens in
// getAddedApkLitesLocked.
- if (android.security.Flags.extendVbChainToUpdatedApk()) {
- if (!isIncrementalInstallation()) {
- enableFsVerityToAddedApksWithIdsig();
- }
+ if (!isIncrementalInstallation()) {
+ enableFsVerityToAddedApksWithIdsig();
}
final List<ApkLite> addedFiles = getAddedApkLitesLocked();
@@ -4124,8 +4122,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
stageFileLocked(origFile, targetFile);
// Stage APK's v4 signature if present, and fs-verity is supported.
- if (android.security.Flags.extendVbChainToUpdatedApk()
- && VerityUtils.isFsVeritySupported()) {
+ if (VerityUtils.isFsVeritySupported()) {
maybeStageV4SignatureLocked(origFile, targetFile);
}
// Stage ART managed install files (e.g., dex metadata (.dm)) and corresponding fs-verity
@@ -4152,9 +4149,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private void inheritFileLocked(File origFile, List<String> artManagedFilePaths) {
mResolvedInheritedFiles.add(origFile);
- if (android.security.Flags.extendVbChainToUpdatedApk()) {
- maybeInheritV4SignatureLocked(origFile);
- }
+ maybeInheritV4SignatureLocked(origFile);
// Inherit ART managed install files (e.g., dex metadata (.dm)) if present.
if (com.android.art.flags.Flags.artServiceV3()) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 3e376b6958ec..2ecdb0b1a02a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -545,8 +545,7 @@ public class PackageManagerServiceUtils {
// Also make sure the parsed signatures are consistent with the disabled package
// setting, if any. The additional UNKNOWN check is because disabled package settings
// may not have SigningDetails currently, and we don't want to cause an uninstall.
- if (android.security.Flags.extendVbChainToUpdatedApk()
- && match && disabledPkgSetting != null
+ if (match && disabledPkgSetting != null
&& disabledPkgSetting.getSigningDetails() != SigningDetails.UNKNOWN) {
match = matchSignatureInSystem(packageName, parsedSignatures, disabledPkgSetting);
}
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 3ece07c84080..0735770f9c3c 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -27,6 +27,7 @@ import android.annotation.Nullable;
import android.app.StatsManager;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -73,7 +74,7 @@ import java.util.List;
import java.util.Set;
/** @hide */
-public class AdvancedProtectionService extends IAdvancedProtectionService.Stub {
+public class AdvancedProtectionService extends IAdvancedProtectionService.Stub {
private static final String TAG = "AdvancedProtectionService";
private static final int MODE_CHANGED = 0;
private static final int CALLBACK_ADDED = 1;
@@ -90,6 +91,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
private final Context mContext;
private final Handler mHandler;
private final AdvancedProtectionStore mStore;
+ private final UserManagerInternal mUserManager;
// Features living with the service - their code will be executed when state changes
private final ArrayList<AdvancedProtectionHook> mHooks = new ArrayList<>();
@@ -106,7 +108,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
super(PermissionEnforcer.fromContext(context));
mContext = context;
mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper());
- mStore = new AdvancedProtectionStore(context);
+ mStore = new AdvancedProtectionStore(mContext);
+ mUserManager = LocalServices.getService(UserManagerInternal.class);
}
private void initFeatures(boolean enabled) {
@@ -156,12 +159,18 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
// Only for tests
@VisibleForTesting
- AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store,
- @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer,
- @Nullable AdvancedProtectionHook hook, @Nullable AdvancedProtectionProvider provider) {
+ AdvancedProtectionService(
+ @NonNull Context context,
+ @NonNull AdvancedProtectionStore store,
+ @NonNull UserManagerInternal userManager,
+ @NonNull Looper looper,
+ @NonNull PermissionEnforcer permissionEnforcer,
+ @Nullable AdvancedProtectionHook hook,
+ @Nullable AdvancedProtectionProvider provider) {
super(permissionEnforcer);
mContext = context;
mStore = store;
+ mUserManager = userManager;
mHandler = new AdvancedProtectionHandler(looper);
if (hook != null) {
mHooks.add(hook);
@@ -218,8 +227,10 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
@EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
public void setAdvancedProtectionEnabled(boolean enabled) {
setAdvancedProtectionEnabled_enforcePermission();
+ final UserHandle user = Binder.getCallingUserHandle();
final long identity = Binder.clearCallingIdentity();
try {
+ enforceAdminUser(user);
synchronized (mCallbacks) {
if (enabled != isAdvancedProtectionEnabledInternal()) {
mStore.storeAdvancedProtectionModeEnabled(enabled);
@@ -364,6 +375,13 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
return features;
}
+ private void enforceAdminUser(UserHandle user) {
+ UserInfo info = mUserManager.getUserInfo(user.getIdentifier());
+ if (!info.isAdmin()) {
+ throw new SecurityException("Only an admin user can manage advanced protection mode");
+ }
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, @NonNull String[] args, ShellCallback callback,
@@ -451,36 +469,34 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
@VisibleForTesting
static class AdvancedProtectionStore {
- private final Context mContext;
static final int ON = 1;
static final int OFF = 0;
- private final UserManagerInternal mUserManager;
+ private final Context mContext;
AdvancedProtectionStore(@NonNull Context context) {
mContext = context;
- mUserManager = LocalServices.getService(UserManagerInternal.class);
}
void storeAdvancedProtectionModeEnabled(boolean enabled) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
ADVANCED_PROTECTION_MODE, enabled ? ON : OFF,
- mUserManager.getMainUserId());
+ UserHandle.USER_SYSTEM);
}
boolean retrieveAdvancedProtectionModeEnabled() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- ADVANCED_PROTECTION_MODE, OFF, mUserManager.getMainUserId()) == ON;
+ ADVANCED_PROTECTION_MODE, OFF, UserHandle.USER_SYSTEM) == ON;
}
void storeInt(String key, int value) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
key, value,
- mUserManager.getMainUserId());
+ UserHandle.USER_SYSTEM);
}
int retrieveInt(String key, int defaultValue) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- key, defaultValue, mUserManager.getMainUserId());
+ key, defaultValue, UserHandle.USER_SYSTEM);
}
}
@@ -492,12 +508,12 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
- //arg1 == enabled
+ // arg1 == enabled
case MODE_CHANGED:
handleAllCallbacks(msg.arg1 == 1);
break;
- //arg1 == enabled
- //obj == callback
+ // arg1 == enabled
+ // obj == callback
case CALLBACK_ADDED:
handleSingleCallback(msg.arg1 == 1, (IAdvancedProtectionCallback) msg.obj);
break;
@@ -546,7 +562,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
private final class DeathRecipient implements IBinder.DeathRecipient {
private final IBinder mBinder;
- DeathRecipient(IBinder binder) {
+ DeathRecipient(IBinder binder) {
mBinder = binder;
}
diff --git a/services/core/java/com/android/server/updates/CertPinInstallReceiver.java b/services/core/java/com/android/server/updates/CertPinInstallReceiver.java
index 250e99b47b1a..c8e7a8dea5c3 100644
--- a/services/core/java/com/android/server/updates/CertPinInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/CertPinInstallReceiver.java
@@ -19,7 +19,10 @@ package com.android.server.updates;
import android.content.Context;
import android.content.Intent;
+import java.io.File;
+
public class CertPinInstallReceiver extends ConfigUpdateInstallReceiver {
+ private static final String KEYCHAIN_DIR = "/data/misc/keychain/";
public CertPinInstallReceiver() {
super("/data/misc/keychain/", "pins", "metadata/", "version");
@@ -27,7 +30,22 @@ public class CertPinInstallReceiver extends ConfigUpdateInstallReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
- if (!com.android.server.flags.Flags.certpininstallerRemoval()) {
+ if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ if (com.android.server.flags.Flags.certpininstallerRemoval()) {
+ File pins = new File(KEYCHAIN_DIR + "pins");
+ if (pins.exists()) {
+ pins.delete();
+ }
+ File version = new File(KEYCHAIN_DIR + "metadata/version");
+ if (version.exists()) {
+ version.delete();
+ }
+ File metadata = new File(KEYCHAIN_DIR + "metadata");
+ if (metadata.exists()) {
+ metadata.delete();
+ }
+ }
+ } else if (!com.android.server.flags.Flags.certpininstallerRemoval()) {
super.onReceive(context, intent);
}
}
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index e612d8ec0ce6..98c143a866d0 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -31,6 +31,7 @@ import android.graphics.Region;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.IntArray;
@@ -260,8 +261,9 @@ public class TrustedPresentationListenerController {
ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates =
new ArrayMap<>();
for (var windowHandle : mLastWindowHandles.first) {
- if (!windowHandle.canOccludePresentation) {
- ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name);
+ var isInvisible = ((windowHandle.inputConfig & InputConfig.NOT_VISIBLE)
+ == InputConfig.NOT_VISIBLE);
+ if (!windowHandle.canOccludePresentation || isInvisible) {
continue;
}
int displayId = INVALID_DISPLAY;
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 9781fb9a1830..e4c214fd93e6 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -27,6 +27,7 @@ import android.credentials.CreateCredentialResponse;
import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.ICreateCredentialCallback;
+import android.credentials.flags.Flags;
import android.credentials.selection.ProviderData;
import android.credentials.selection.RequestInfo;
import android.os.CancellationSignal;
@@ -145,6 +146,9 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
if (response != null) {
mRequestSessionMetric.collectChosenProviderStatus(
ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
+ if (Flags.fixMetricDuplicationEmits()) {
+ mRequestSessionMetric.collectChosenClassType(mClientRequest.getType());
+ }
respondToClientWithResponseAndFinish(response);
} else {
mRequestSessionMetric.collectChosenProviderStatus(
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index be36b6c5690b..fd00f6dde815 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -27,6 +27,7 @@ import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
import android.credentials.IGetCredentialCallback;
+import android.credentials.flags.Flags;
import android.credentials.selection.ProviderData;
import android.credentials.selection.RequestInfo;
import android.os.Binder;
@@ -146,6 +147,12 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
if (response != null) {
mRequestSessionMetric.collectChosenProviderStatus(
ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
+ if (Flags.fixMetricDuplicationEmits()) {
+ if (response.getCredential() != null) {
+ mRequestSessionMetric.collectChosenClassType(response.getCredential()
+ .getType());
+ }
+ }
respondToClientWithResponseAndFinish(response);
} else {
mRequestSessionMetric.collectChosenProviderStatus(
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 11edb93dffea..9c89f4c31fc6 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -201,7 +201,8 @@ public class MetricUtilities {
finalPhaseMetric.getResponseCollective().getUniqueResponseCounts(),
/* framework_exception_unique_classtype */
finalPhaseMetric.getFrameworkException(),
- /* primary_indicated */ finalPhaseMetric.isPrimary()
+ /* primary_indicated */ finalPhaseMetric.isPrimary(),
+ /* chosen_classtype */ finalPhaseMetric.getChosenClassType()
);
} catch (Exception e) {
Slog.w(TAG, "Unexpected error during final provider uid emit: " + e);
@@ -587,7 +588,8 @@ public class MetricUtilities {
/* primary_indicated */ finalPhaseMetric.isPrimary(),
/* oem_credential_manager_ui_uid */ finalPhaseMetric.getOemUiUid(),
/* fallback_credential_manager_ui_uid */ finalPhaseMetric.getFallbackUiUid(),
- /* oem_ui_usage_status */ finalPhaseMetric.getOemUiUsageStatus()
+ /* oem_ui_usage_status */ finalPhaseMetric.getOemUiUsageStatus(),
+ /* chosen_classtype */ finalPhaseMetric.getChosenClassType()
);
} catch (Exception e) {
Slog.w(TAG, "Unexpected error during final no uid metric logging: " + e);
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
index 9dd6db6f9d6a..c8f6ba06d020 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
@@ -83,12 +83,24 @@ public class ChosenProviderFinalPhaseMetric {
// Indicates if this chosen provider was the primary provider, false by default
private boolean mIsPrimary = false;
+ private String mChosenClassType = "";
+
public ChosenProviderFinalPhaseMetric(int sessionIdCaller, int sessionIdProvider) {
mSessionIdCaller = sessionIdCaller;
mSessionIdProvider = sessionIdProvider;
}
+ /* ------------------- Chosen Credential ------------------- */
+
+ public void setChosenClassType(String clickedClassType) {
+ mChosenClassType = clickedClassType;
+ }
+
+ public String getChosenClassType() {
+ return mChosenClassType;
+ }
+
/* ------------------- UID ------------------- */
public int getChosenUid() {
diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
index dc1747f803ea..3d740e531e14 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
@@ -262,6 +262,21 @@ public class RequestSessionMetric {
}
/**
+ * This collects the final chosen class type. While it is possible to collect this during
+ * browsing, note this only collects the final tapped bit.
+ *
+ * @param createOrCredentialType the string type to collect when an entry is tapped by the user
+ */
+ public void collectChosenClassType(String createOrCredentialType) {
+ String truncatedType = generateMetricKey(createOrCredentialType, DELTA_EXCEPTION_CUT);
+ try {
+ mChosenProviderFinalPhaseMetric.setChosenClassType(truncatedType);
+ } catch (Exception e) {
+ Slog.i(TAG, "Unexpected error collecting chosen class type metadata: " + e);
+ }
+ }
+
+ /**
* Updates the final phase metric with the designated bit.
*
* @param exceptionBitFinalPhase represents if the final phase provider had an exception
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 5a140d53a4d8..662e0c06f261 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -100,7 +100,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
override fun MutateStateScope.onStorageVolumeMounted(
volumeUuid: String?,
packageNames: List<String>,
- isSystemUpdated: Boolean
+ isSystemUpdated: Boolean,
) {
val changedPermissionNames = MutableIndexedSet<String>()
packageNames.forEachIndexed { _, packageName ->
@@ -173,7 +173,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.clearRestrictedPermissionImplicitExemption(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
// System apps can always retain their UPGRADE_EXEMPT.
if (packageState.isSystem) {
@@ -198,7 +198,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
userId,
permission,
PermissionFlags.UPGRADE_EXEMPT,
- 0
+ 0,
)
}
}
@@ -208,7 +208,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
userId: Int,
permission: Permission,
exemptFlagMask: Int,
- exemptFlagValues: Int
+ exemptFlagValues: Int,
) {
val permissionName = permission.name
val oldFlags = getPermissionFlags(appId, userId, permissionName)
@@ -236,7 +236,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
isSoftRestrictedPermissionExemptForPackage(
it,
targetSdkVersion,
- permissionName
+ permissionName,
)
}
} else {
@@ -257,7 +257,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
override fun MutateStateScope.onPackageUninstalled(
packageName: String,
appId: Int,
- userId: Int
+ userId: Int,
) {
resetRuntimePermissions(packageName, userId)
}
@@ -290,17 +290,16 @@ class AppIdPermissionPolicy : SchemePolicy() {
packageState.isSystem || packageState.getUserStateOrDefault(userId).isInstalled
newFlags =
if (
- isSystemOrInstalled && (
- newFlags.hasBits(PermissionFlags.ROLE) ||
- newFlags.hasBits(PermissionFlags.PREGRANT)
- )
+ isSystemOrInstalled &&
+ (newFlags.hasBits(PermissionFlags.ROLE) ||
+ newFlags.hasBits(PermissionFlags.PREGRANT))
) {
newFlags or PermissionFlags.RUNTIME_GRANTED
} else {
- newFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or PermissionFlags.ROLE or
- PermissionFlags.PREGRANT
- )
+ newFlags andInv
+ (PermissionFlags.RUNTIME_GRANTED or
+ PermissionFlags.ROLE or
+ PermissionFlags.PREGRANT)
}
newFlags = newFlags andInv USER_SETTABLE_MASK
if (newFlags.hasBits(PermissionFlags.LEGACY_GRANTED)) {
@@ -312,7 +311,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.adoptPermissions(
packageState: PackageState,
- changedPermissionNames: MutableIndexedSet<String>
+ changedPermissionNames: MutableIndexedSet<String>,
) {
val `package` = packageState.androidPackage!!
`package`.adoptPermissions.forEachIndexed { _, originalPackageName ->
@@ -341,7 +340,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
oldPermission.copy(
permissionInfo = newPermissionInfo,
isReconciled = false,
- appId = 0
+ appId = 0,
)
newState
.mutateSystemState()
@@ -354,7 +353,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.canAdoptPermissions(
packageName: String,
- originalPackageName: String
+ originalPackageName: String,
): Boolean {
val originalPackageState =
newState.externalState.packageStates[originalPackageName] ?: return false
@@ -362,7 +361,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Slog.w(
LOG_TAG,
"Unable to adopt permissions from $originalPackageName to $packageName:" +
- " original package not in system partition"
+ " original package not in system partition",
)
return false
}
@@ -370,7 +369,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Slog.w(
LOG_TAG,
"Unable to adopt permissions from $originalPackageName to $packageName:" +
- " original package still exists"
+ " original package still exists",
)
return false
}
@@ -386,7 +385,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Slog.w(
LOG_TAG,
"Ignoring permission groups declared in package" +
- " ${packageState.packageName}: instant apps cannot declare permission groups"
+ " ${packageState.packageName}: instant apps cannot declare permission groups",
)
return
}
@@ -394,7 +393,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
val newPermissionGroup =
PackageInfoUtils.generatePermissionGroupInfo(
parsedPermissionGroup,
- PackageManager.GET_META_DATA.toLong()
+ PackageManager.GET_META_DATA.toLong(),
)!!
// TODO: Clear permission state on group take-over?
val permissionGroupName = newPermissionGroup.name
@@ -414,7 +413,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Ignoring permission group $permissionGroupName declared in" +
" package $newPackageName: already declared in another" +
- " package $oldPackageName"
+ " package $oldPackageName",
)
return@forEachIndexed
}
@@ -423,7 +422,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Ignoring permission group $permissionGroupName declared in" +
" system package $newPackageName: already declared in another" +
- " system package $oldPackageName"
+ " system package $oldPackageName",
)
return@forEachIndexed
}
@@ -431,7 +430,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Overriding permission group $permissionGroupName with" +
" new declaration in system package $newPackageName: originally" +
- " declared in another package $oldPackageName"
+ " declared in another package $oldPackageName",
)
}
newState.mutateSystemState().mutatePermissionGroups()[permissionGroupName] =
@@ -441,7 +440,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.addPermissions(
packageState: PackageState,
- changedPermissionNames: MutableIndexedSet<String>
+ changedPermissionNames: MutableIndexedSet<String>,
) {
val androidPackage = packageState.androidPackage!!
// This may not be the same package as the old permission because the old permission owner
@@ -454,7 +453,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
val newPermissionInfo =
PackageInfoUtils.generatePermissionInfo(
parsedPermission,
- PackageManager.GET_META_DATA.toLong()
+ PackageManager.GET_META_DATA.toLong(),
)!!
val permissionName = newPermissionInfo.name
val oldPermission =
@@ -474,7 +473,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Ignoring permission $permissionName declared in package" +
" $newPackageName: base permission tree ${permissionTree.name} is" +
- " declared in another package ${permissionTree.packageName}"
+ " declared in another package ${permissionTree.packageName}",
)
return@forEachIndexed
}
@@ -488,7 +487,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Ignoring permission $permissionName declared in package" +
" $newPackageName: already declared in another package" +
- " $oldPackageName"
+ " $oldPackageName",
)
return@forEachIndexed
}
@@ -497,7 +496,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Ignoring permission $permissionName declared in system package" +
" $newPackageName: already declared in another system package" +
- " $oldPackageName"
+ " $oldPackageName",
)
return@forEachIndexed
}
@@ -505,7 +504,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Overriding permission $permissionName with new declaration in" +
" system package $newPackageName: originally declared in another" +
- " package $oldPackageName"
+ " package $oldPackageName",
)
// Remove permission state on owner change.
newState.externalState.userIds.forEachIndexed { _, userId ->
@@ -534,7 +533,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
"Revoking runtime permission $permissionName for" +
" appId $appId and userId $userId as the permission" +
" group changed from ${oldPermission.groupName}" +
- " to ${newPermissionInfo.group}"
+ " to ${newPermissionInfo.group}",
)
}
if (isPermissionProtectionChanged) {
@@ -542,7 +541,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Revoking permission $permissionName for" +
" appId $appId and userId $userId as the permission" +
- " protection changed."
+ " protection changed.",
)
}
setPermissionFlags(appId, userId, permissionName, 0)
@@ -572,7 +571,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Permission.TYPE_MANIFEST,
packageState.appId,
gids,
- areGidsPerUser
+ areGidsPerUser,
)
if (parsedPermission.isTree) {
@@ -599,7 +598,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.trimPermissions(
packageName: String,
- changedPermissionNames: MutableIndexedSet<String>
+ changedPermissionNames: MutableIndexedSet<String>,
) {
val packageState = newState.externalState.packageStates[packageName]
val androidPackage = packageState?.androidPackage
@@ -675,7 +674,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
packageName = permissionTree.packageName
},
appId = permissionTree.appId,
- isReconciled = true
+ isReconciled = true,
)
}
@@ -754,7 +753,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Slog.v(
LOG_TAG,
"Revoking storage permission: $permissionName for appId: " +
- " $appId and user: $userId"
+ " $appId and user: $userId",
)
val newFlags =
oldFlags andInv (PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK)
@@ -767,7 +766,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.evaluatePermissionStateForAllPackages(
permissionName: String,
- installedPackageState: PackageState?
+ installedPackageState: PackageState?,
) {
val externalState = newState.externalState
externalState.userIds.forEachIndexed { _, userId ->
@@ -785,13 +784,13 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.evaluateAllPermissionStatesForPackage(
packageState: PackageState,
- installedPackageState: PackageState?
+ installedPackageState: PackageState?,
) {
newState.externalState.userIds.forEachIndexed { _, userId ->
evaluateAllPermissionStatesForPackageAndUser(
packageState,
userId,
- installedPackageState
+ installedPackageState,
)
}
}
@@ -799,14 +798,14 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.evaluateAllPermissionStatesForPackageAndUser(
packageState: PackageState,
userId: Int,
- installedPackageState: PackageState?
+ installedPackageState: PackageState?,
) {
packageState.androidPackage?.requestedPermissions?.forEach { permissionName ->
evaluatePermissionState(
packageState.appId,
userId,
permissionName,
- installedPackageState
+ installedPackageState,
)
}
}
@@ -815,7 +814,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
appId: Int,
userId: Int,
permissionName: String,
- installedPackageState: PackageState?
+ installedPackageState: PackageState?,
) {
val packageNames = newState.externalState.appIdPackageNames[appId]!!
// Repeatedly checking whether a permission is requested can actually be costly, so we cache
@@ -989,8 +988,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
"Unknown source permission $sourcePermissionName in split permissions"
}
!sourcePermission.isRuntime
- }
- ?: false
+ } ?: false
val shouldGrantByImplicit =
isLeanbackNotificationsPermission ||
(isImplicitPermission && isAnySourcePermissionNonRuntime)
@@ -1024,7 +1022,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
getPermissionFlags(
appId,
userId,
- Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION,
)
shouldRetainAsNearbyDevices =
PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) &&
@@ -1081,7 +1079,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
isSoftRestrictedPermissionExemptForPackage(
it,
targetSdkVersion,
- permissionName
+ permissionName,
)
}
) {
@@ -1095,7 +1093,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Unknown protection level ${permission.protectionLevel}" +
"for permission ${permission.name} while evaluating permission state" +
- "for appId $appId and userId $userId"
+ "for appId $appId and userId $userId",
)
}
}
@@ -1154,7 +1152,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun isCompatibilityPermissionForPackage(
androidPackage: AndroidPackage,
- permissionName: String
+ permissionName: String,
): Boolean {
for (compatibilityPermission in CompatibilityPermissionInfo.COMPAT_PERMS) {
if (
@@ -1164,7 +1162,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Slog.i(
LOG_TAG,
"Auto-granting $permissionName to old package" +
- " ${androidPackage.packageName}"
+ " ${androidPackage.packageName}",
)
return true
}
@@ -1174,7 +1172,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.shouldGrantPermissionBySignature(
packageState: PackageState,
- permission: Permission
+ permission: Permission,
): Boolean {
// Check if the package is allowed to use this signature permission. A package is allowed
// to use a signature permission if:
@@ -1197,12 +1195,12 @@ class AppIdPermissionPolicy : SchemePolicy() {
val hasCommonSigner =
sourceSigningDetails?.hasCommonSignerWithCapability(
packageSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION
+ SigningDetails.CertCapabilities.PERMISSION,
) == true ||
packageSigningDetails.hasAncestorOrSelf(platformSigningDetails) ||
platformSigningDetails.checkCapability(
packageSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION
+ SigningDetails.CertCapabilities.PERMISSION,
)
if (!Flags.signaturePermissionAllowlistEnabled()) {
return hasCommonSigner
@@ -1237,7 +1235,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Signature permission ${permission.name} for package" +
" ${packageState.packageName} (${packageState.path}) not in" +
- " signature permission allowlist"
+ " signature permission allowlist",
)
if (!Build.isDebuggable() || isSignaturePermissionAllowlistForceEnforced) {
return false
@@ -1249,7 +1247,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.getSignaturePermissionAllowlistState(
packageState: PackageState,
- permissionName: String
+ permissionName: String,
): Boolean? {
val permissionAllowlist = newState.externalState.permissionAllowlist
val packageName = packageState.packageName
@@ -1259,30 +1257,30 @@ class AppIdPermissionPolicy : SchemePolicy() {
packageState.isProduct ->
permissionAllowlist.getProductSignatureAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
packageState.isSystemExt ->
permissionAllowlist.getSystemExtSignatureAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
else ->
permissionAllowlist.getApexSignatureAppAllowlistState(packageName, permissionName)
?: permissionAllowlist.getProductSignatureAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
?: permissionAllowlist.getVendorSignatureAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
?: permissionAllowlist.getSystemExtSignatureAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
?: permissionAllowlist.getSignatureAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
}
}
@@ -1292,13 +1290,13 @@ class AppIdPermissionPolicy : SchemePolicy() {
* or for normal apps, we return true to indicate that we don't need to check the allowlist and
* will let follow-up checks to decide whether we should grant the permission.
*
- * @return `true`, if the permission is allowlisted for system privileged apps, or if we
- * don't need to check the allowlist (for platform or for normal apps).
- * `false`, if the permission is not allowlisted for system privileged apps.
+ * @return `true`, if the permission is allowlisted for system privileged apps, or if we don't
+ * need to check the allowlist (for platform or for normal apps). `false`, if the permission
+ * is not allowlisted for system privileged apps.
*/
private fun MutateStateScope.checkPrivilegedPermissionAllowlistIfNeeded(
packageState: PackageState,
- permission: Permission
+ permission: Permission,
): Boolean {
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
return true
@@ -1330,7 +1328,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Privileged permission ${permission.name} for package" +
" ${packageState.packageName} (${packageState.path}) not in" +
- " privileged permission allowlist"
+ " privileged permission allowlist",
)
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
privilegedPermissionAllowlistViolations +=
@@ -1348,7 +1346,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
*/
private fun MutateStateScope.getPrivilegedPermissionAllowlistState(
packageState: PackageState,
- permissionName: String
+ permissionName: String,
): Boolean? {
val permissionAllowlist = newState.externalState.permissionAllowlist
val apexModuleName = packageState.apexModuleName
@@ -1357,17 +1355,17 @@ class AppIdPermissionPolicy : SchemePolicy() {
packageState.isVendor || packageState.isOdm ->
permissionAllowlist.getVendorPrivilegedAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
packageState.isProduct ->
permissionAllowlist.getProductPrivilegedAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
packageState.isSystemExt ->
permissionAllowlist.getSystemExtPrivilegedAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
apexModuleName != null -> {
val nonApexAllowlistState =
@@ -1379,14 +1377,14 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Package $packageName is an APK in APEX but has permission" +
" allowlist on the system image, please bundle the allowlist in the" +
- " $apexModuleName APEX instead"
+ " $apexModuleName APEX instead",
)
}
val apexAllowlistState =
permissionAllowlist.getApexPrivilegedAppAllowlistState(
apexModuleName,
packageName,
- permissionName
+ permissionName,
)
apexAllowlistState ?: nonApexAllowlistState
}
@@ -1403,7 +1401,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun isSoftRestrictedPermissionExemptForPackage(
packageState: PackageState,
appIdTargetSdkVersion: Int,
- permissionName: String
+ permissionName: String,
): Boolean =
when (permissionName) {
Manifest.permission.READ_EXTERNAL_STORAGE,
@@ -1415,7 +1413,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.getAppIdTargetSdkVersion(
appId: Int,
permissionName: String?,
- state: AccessState = newState
+ state: AccessState = newState,
): Int =
reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, state) {
targetSdkVersion,
@@ -1431,7 +1429,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private inline fun MutateStateScope.anyPackageInAppId(
appId: Int,
state: AccessState = newState,
- predicate: (PackageState) -> Boolean
+ predicate: (PackageState) -> Boolean,
): Boolean {
val packageNames = state.externalState.appIdPackageNames[appId]!!
return packageNames.anyIndexed { _, packageName ->
@@ -1443,7 +1441,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private inline fun MutateStateScope.forEachPackageInAppId(
appId: Int,
state: AccessState = newState,
- action: (PackageState) -> Unit
+ action: (PackageState) -> Unit,
) {
val packageNames = state.externalState.appIdPackageNames[appId]!!
packageNames.forEachIndexed { _, packageName ->
@@ -1459,7 +1457,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
appId: Int,
initialValue: Int,
state: AccessState = newState,
- accumulator: (Int, PackageState) -> Int
+ accumulator: (Int, PackageState) -> Int,
): Int {
val packageNames = state.externalState.appIdPackageNames[appId]!!
return packageNames.reduceIndexed(initialValue) { value, _, packageName ->
@@ -1474,7 +1472,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.shouldGrantPermissionByProtectionFlags(
packageState: PackageState,
- permission: Permission
+ permission: Permission,
): Boolean {
val androidPackage = packageState.androidPackage!!
val knownPackages = newState.externalState.knownPackages
@@ -1587,7 +1585,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.shouldGrantPrivilegedOrOemPermission(
packageState: PackageState,
- permission: Permission
+ permission: Permission,
): Boolean {
val permissionName = permission.name
val packageName = packageState.packageName
@@ -1605,7 +1603,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
LOG_TAG,
"Permission $permissionName cannot be granted to privileged" +
" vendor (or odm) app $packageName because it isn't a" +
- " vendorPrivileged permission"
+ " vendorPrivileged permission",
)
return false
}
@@ -1617,7 +1615,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
val allowlistState =
newState.externalState.permissionAllowlist.getOemAppAllowlistState(
packageName,
- permissionName
+ permissionName,
)
checkNotNull(allowlistState) {
"OEM permission $permissionName requested by package" +
@@ -1688,7 +1686,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
fun MutateStateScope.addPermission(
permission: Permission,
- isSynchronousWrite: Boolean = false
+ isSynchronousWrite: Boolean = false,
) {
val writeMode = if (isSynchronousWrite) WriteMode.SYNCHRONOUS else WriteMode.ASYNCHRONOUS
newState.mutateSystemState(writeMode).mutatePermissions()[permission.name] = permission
@@ -1707,14 +1705,14 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.getOldStatePermissionFlags(
appId: Int,
userId: Int,
- permissionName: String
+ permissionName: String,
): Int = getPermissionFlags(oldState, appId, userId, permissionName)
private fun getPermissionFlags(
state: AccessState,
appId: Int,
userId: Int,
- permissionName: String
+ permissionName: String,
): Int =
state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
@@ -1725,7 +1723,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
appId: Int,
userId: Int,
permissionName: String,
- flags: Int
+ flags: Int,
): Boolean =
updatePermissionFlags(appId, userId, permissionName, PermissionFlags.MASK_ALL, flags)
@@ -1734,7 +1732,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
userId: Int,
permissionName: String,
flagMask: Int,
- flagValues: Int
+ flagValues: Int,
): Boolean {
if (userId !in newState.userStates) {
// Despite that we check UserManagerInternal.exists() in PermissionService, we may still
@@ -1793,7 +1791,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
override fun MutateStateScope.upgradePackageState(
packageState: PackageState,
userId: Int,
- version: Int
+ version: Int,
) {
with(upgrade) { upgradePackageState(packageState, userId, version) }
}
@@ -1819,7 +1817,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Manifest.permission.BLUETOOTH_ADVERTISE,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
- Manifest.permission.NEARBY_WIFI_DEVICES
+ Manifest.permission.NEARBY_WIFI_DEVICES,
)
private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(Manifest.permission.POST_NOTIFICATIONS)
@@ -1832,7 +1830,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
)
/** Mask for all permission flags that can be set by the user */
@@ -1866,7 +1864,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
userId: Int,
permissionName: String,
oldFlags: Int,
- newFlags: Int
+ newFlags: Int,
)
/**
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
index a4546aebef21..5d8886a9e2d9 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
@@ -36,14 +36,14 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
fun MutateStateScope.upgradePackageState(
packageState: PackageState,
userId: Int,
- version: Int
+ version: Int,
) {
val packageName = packageState.packageName
if (version <= 3) {
Slog.v(
LOG_TAG,
"Allowlisting and upgrading background location permission for " +
- "package: $packageName, version: $version, user:$userId"
+ "package: $packageName, version: $version, user:$userId",
)
allowlistRestrictedPermissions(packageState, userId)
upgradeBackgroundLocationPermission(packageState, userId)
@@ -52,7 +52,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
Slog.v(
LOG_TAG,
"Upgrading access media location permission for package: $packageName" +
- ", version: $version, user: $userId"
+ ", version: $version, user: $userId",
)
upgradeAccessMediaLocationPermission(packageState, userId)
}
@@ -61,7 +61,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
Slog.v(
LOG_TAG,
"Upgrading scoped media and body sensor permissions for package: $packageName" +
- ", version: $version, user: $userId"
+ ", version: $version, user: $userId",
)
upgradeAuralVisualMediaPermissions(packageState, userId)
upgradeBodySensorPermissions(packageState, userId)
@@ -71,7 +71,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
Slog.v(
LOG_TAG,
"Upgrading visual media permission for package: $packageName" +
- ", version: $version, user: $userId"
+ ", version: $version, user: $userId",
)
upgradeUserSelectedVisualMediaPermission(packageState, userId)
}
@@ -81,7 +81,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
private fun MutateStateScope.allowlistRestrictedPermissions(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
packageState.androidPackage!!.requestedPermissions.forEach { permissionName ->
if (permissionName in LEGACY_RESTRICTED_PERMISSIONS) {
@@ -91,7 +91,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
userId,
permissionName,
PermissionFlags.UPGRADE_EXEMPT,
- PermissionFlags.UPGRADE_EXEMPT
+ PermissionFlags.UPGRADE_EXEMPT,
)
}
}
@@ -100,7 +100,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
private fun MutateStateScope.upgradeBackgroundLocationPermission(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
if (
Manifest.permission.ACCESS_BACKGROUND_LOCATION in
@@ -122,7 +122,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
grantRuntimePermission(
packageState,
userId,
- Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION,
)
}
}
@@ -130,7 +130,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
private fun MutateStateScope.upgradeAccessMediaLocationPermission(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
if (
Manifest.permission.ACCESS_MEDIA_LOCATION in
@@ -141,14 +141,14 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
getPermissionFlags(
packageState.appId,
userId,
- Manifest.permission.READ_EXTERNAL_STORAGE
+ Manifest.permission.READ_EXTERNAL_STORAGE,
)
}
if (PermissionFlags.isAppOpGranted(flags)) {
grantRuntimePermission(
packageState,
userId,
- Manifest.permission.ACCESS_MEDIA_LOCATION
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
)
}
}
@@ -157,7 +157,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
/** Upgrade permissions based on storage permissions grant */
private fun MutateStateScope.upgradeAuralVisualMediaPermissions(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
val androidPackage = packageState.androidPackage!!
if (androidPackage.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) {
@@ -184,7 +184,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
private fun MutateStateScope.upgradeBodySensorPermissions(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
if (
Manifest.permission.BODY_SENSORS_BACKGROUND !in
@@ -221,7 +221,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
grantRuntimePermission(
packageState,
userId,
- Manifest.permission.BODY_SENSORS_BACKGROUND
+ Manifest.permission.BODY_SENSORS_BACKGROUND,
)
}
}
@@ -229,7 +229,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
/** Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL] */
private fun MutateStateScope.upgradeUserSelectedVisualMediaPermission(
packageState: PackageState,
- userId: Int
+ userId: Int,
) {
val androidPackage = packageState.androidPackage!!
if (androidPackage.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) {
@@ -250,7 +250,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
grantRuntimePermission(
packageState,
userId,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
)
}
}
@@ -259,12 +259,12 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
private fun MutateStateScope.grantRuntimePermission(
packageState: PackageState,
userId: Int,
- permissionName: String
+ permissionName: String,
) {
Slog.v(
LOG_TAG,
"Granting runtime permission for package: ${packageState.packageName}, " +
- "permission: $permissionName, userId: $userId"
+ "permission: $permissionName, userId: $userId",
)
val permission = newState.systemState.permissions[permissionName]!!
if (packageState.getUserStateOrDefault(userId).isInstantApp && !permission.isInstant) {
@@ -276,7 +276,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
if (flags.hasAnyBit(MASK_ANY_FIXED)) {
Slog.v(
LOG_TAG,
- "Not allowed to grant $permissionName to package ${packageState.packageName}"
+ "Not allowed to grant $permissionName to package ${packageState.packageName}",
)
return
}
@@ -314,13 +314,13 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
Manifest.permission.READ_CELL_BROADCASTS,
Manifest.permission.READ_CALL_LOG,
Manifest.permission.WRITE_CALL_LOG,
- Manifest.permission.PROCESS_OUTGOING_CALLS
+ Manifest.permission.PROCESS_OUTGOING_CALLS,
)
private val STORAGE_PERMISSIONS =
indexedSetOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
private val AURAL_VISUAL_MEDIA_PERMISSIONS =
indexedSetOf(
@@ -328,14 +328,14 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
)
// Visual media permissions in T
private val VISUAL_MEDIA_PERMISSIONS =
indexedSetOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.ACCESS_MEDIA_LOCATION
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
)
}
}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt
index 304f605d5b95..7c4515962669 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt
@@ -16,8 +16,6 @@
package com.android.server.pm.test
-import android.platform.test.annotations.RequiresFlagsEnabled
-import android.platform.test.flag.junit.host.HostFlagsValueProvider
import com.android.internal.util.test.SystemPreparer
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
@@ -35,7 +33,6 @@ import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
@RunWith(DeviceJUnit4ClassRunner::class)
-@RequiresFlagsEnabled(android.security.Flags.FLAG_EXTEND_VB_CHAIN_TO_UPDATED_APK)
class TamperedUpdatedSystemPackageTest : BaseHostJUnit4Test() {
companion object {
@@ -68,10 +65,6 @@ class TamperedUpdatedSystemPackageTest : BaseHostJUnit4Test() {
@Rule
@JvmField
- val checkFlagsRule = HostFlagsValueProvider.createCheckFlagsRule({ getDevice() })
-
- @Rule
- @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
@Before
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
index 6b9c9c2b4abc..bf9033981442 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
@@ -57,10 +57,10 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests a normal permission" +
- " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a normal permission" +
+ " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -71,16 +71,16 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
testEvaluatePermissionState(
oldFlags,
PermissionInfo.PROTECTION_NORMAL,
- isNewInstall = true
+ isNewInstall = true,
) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a normal permission" +
- " with no existing flags, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a normal permission" +
+ " with no existing flags, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -90,16 +90,16 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val oldFlags = PermissionFlags.ROLE or PermissionFlags.USER_SET
testEvaluatePermissionState(
oldFlags,
- PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP
+ PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP,
) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.INSTALL_GRANTED or oldFlags
assertWithMessage(
- "After $action is called for a package that requests a normal app op" +
- " permission with existing ROLE and USER_SET flags, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a normal app op" +
+ " permission with existing ROLE and USER_SET flags, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -115,21 +115,21 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@Test
fun testEvaluatePermissionState_internalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
- val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
- PermissionFlags.USER_SET
+ val oldFlags =
+ PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or PermissionFlags.USER_SET
testEvaluatePermissionState(
oldFlags,
- PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP
+ PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP,
) {
val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
addPackageState(packageStateWithMissingPackage)
@@ -138,11 +138,11 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag and the permission isAppOp," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag and the permission isAppOp," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -152,7 +152,7 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
testEvaluatePermissionState(
oldFlags,
- PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT
+ PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT,
) {
val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
addPackageState(packageStateWithMissingPackage)
@@ -161,22 +161,24 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag and permission isDevelopment," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag and permission isDevelopment," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@Test
fun testEvaluatePermissionState_internalRolePermission_getsRoleAndRuntimeGrantedPreserved() {
- val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
- PermissionFlags.RUNTIME_GRANTED
+ val oldFlags =
+ PermissionFlags.PROTECTION_GRANTED or
+ PermissionFlags.ROLE or
+ PermissionFlags.RUNTIME_GRANTED
testEvaluatePermissionState(
oldFlags,
- PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE
+ PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE,
) {
val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
addPackageState(packageStateWithMissingPackage)
@@ -185,11 +187,11 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag and the permission isRole," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag and the permission isRole," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -205,12 +207,10 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
isInstalledPackageProduct = true,
// To mock the return value of shouldGrantPrivilegedOrOemPermission()
isInstalledPackageVendor = true,
- isNewInstall = true
+ isNewInstall = true,
) {
- val platformPackage = mockPackageState(
- PLATFORM_APP_ID,
- mockAndroidPackage(PLATFORM_PACKAGE_NAME)
- )
+ val platformPackage =
+ mockPackageState(PLATFORM_APP_ID, mockAndroidPackage(PLATFORM_PACKAGE_NAME))
setupAllowlist(PACKAGE_NAME_1, false)
addPackageState(platformPackage)
}
@@ -218,10 +218,10 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests a signature privileged" +
- " permission that's not allowlisted, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a signature privileged" +
+ " permission that's not allowlisted, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -237,12 +237,13 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
isInstalledPackageProduct = true,
isInstalledPackageSignatureMatching = true,
isInstalledPackageVendor = true,
- isNewInstall = true
+ isNewInstall = true,
) {
- val platformPackage = mockPackageState(
- PLATFORM_APP_ID,
- mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true)
- )
+ val platformPackage =
+ mockPackageState(
+ PLATFORM_APP_ID,
+ mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true),
+ )
setupAllowlist(PACKAGE_NAME_1, false)
addPackageState(platformPackage)
}
@@ -250,10 +251,10 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a signature" +
- " non-privileged permission, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a signature" +
+ " non-privileged permission, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -267,12 +268,10 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
isInstalledPackageSystem = true,
isInstalledPackagePrivileged = true,
isInstalledPackageProduct = true,
- isNewInstall = true
+ isNewInstall = true,
) {
- val platformPackage = mockPackageState(
- PLATFORM_APP_ID,
- mockAndroidPackage(PLATFORM_PACKAGE_NAME)
- )
+ val platformPackage =
+ mockPackageState(PLATFORM_APP_ID, mockAndroidPackage(PLATFORM_PACKAGE_NAME))
setupAllowlist(PACKAGE_NAME_1, true)
addPackageState(platformPackage)
}
@@ -280,10 +279,10 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a signature privileged" +
- " permission that's allowlisted and should grant by protection flags, the actual" +
- " permission flags $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a signature privileged" +
+ " permission that's allowlisted and should grant by protection flags, the actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -291,32 +290,36 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
private fun setupAllowlist(
packageName: String,
allowlistState: Boolean,
- state: MutableAccessState = oldState
+ state: MutableAccessState = oldState,
) {
- state.mutateExternalState().setPrivilegedPermissionAllowlistPackages(
- MutableIndexedListSet<String>().apply { add(packageName) }
- )
- val mockAllowlist = mock<PermissionAllowlist> {
- whenever(
- getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0)
- ).thenReturn(allowlistState)
- }
+ state
+ .mutateExternalState()
+ .setPrivilegedPermissionAllowlistPackages(
+ MutableIndexedListSet<String>().apply { add(packageName) }
+ )
+ val mockAllowlist =
+ mock<PermissionAllowlist> {
+ whenever(getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0))
+ .thenReturn(allowlistState)
+ }
state.mutateExternalState().setPermissionAllowlist(mockAllowlist)
}
@Test
fun testEvaluatePermissionState_nonRuntimeFlagsOnRuntimePermissions_getsCleared() {
- val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.PREGRANT or
- PermissionFlags.RUNTIME_GRANTED
+ val oldFlags =
+ PermissionFlags.INSTALL_GRANTED or
+ PermissionFlags.PREGRANT or
+ PermissionFlags.RUNTIME_GRANTED
testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.PREGRANT or PermissionFlags.RUNTIME_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " with existing $oldFlags flags, the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " with existing $oldFlags flags, the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -328,16 +331,16 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP,
- isNewInstall = true
+ isNewInstall = true,
) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " with no existing flags in pre M, actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " with no existing flags in pre M, actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -348,20 +351,22 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
testEvaluatePermissionState(
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
- installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP
+ installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP,
) {
setPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0, oldFlags)
}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.USER_FIXED or
- PermissionFlags.APP_OP_REVOKED
+ val expectedNewFlags =
+ PermissionFlags.LEGACY_GRANTED or
+ PermissionFlags.USER_FIXED or
+ PermissionFlags.APP_OP_REVOKED
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," +
- " the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," +
+ " the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -374,11 +379,11 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = 0
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " that used to require user review, the user review requirement should be removed" +
- " if it's upgraded to post M. The actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " that used to require user review, the user review requirement should be removed" +
+ " if it's upgraded to post M. The actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -391,11 +396,11 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" +
- " if it's upgraded to post M. The actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" +
+ " if it's upgraded to post M. The actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -407,22 +412,19 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
permissionName = PERMISSION_POST_NOTIFICATIONS,
- isNewInstall = true
+ isNewInstall = true,
) {
oldState.mutateExternalState().setLeanback(true)
}
- val actualFlags = getPermissionFlags(
- APP_ID_1,
- getUserIdEvaluated(),
- PERMISSION_POST_NOTIFICATIONS
- )
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_POST_NOTIFICATIONS)
val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a runtime notification" +
- " permission when isLeanback, the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime notification" +
+ " permission when isLeanback, the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -434,65 +436,73 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
implicitPermissions = setOf(PERMISSION_NAME_0),
- isNewInstall = true
+ isNewInstall = true,
) {
- oldState.mutateExternalState().setImplicitToSourcePermissions(
- MutableIndexedMap<String, IndexedListSet<String>>().apply {
- put(PERMISSION_NAME_0, MutableIndexedListSet<String>().apply {
- add(PERMISSION_NAME_1)
- })
- }
- )
+ oldState
+ .mutateExternalState()
+ .setImplicitToSourcePermissions(
+ MutableIndexedMap<String, IndexedListSet<String>>().apply {
+ put(
+ PERMISSION_NAME_0,
+ MutableIndexedListSet<String>().apply { add(PERMISSION_NAME_1) },
+ )
+ }
+ )
addPermission(mockParsedPermission(PERMISSION_NAME_1, PACKAGE_NAME_0))
}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
assertWithMessage(
- "After $action is called for a package that requests a runtime implicit" +
- " permission that's source from a non-runtime permission, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime implicit" +
+ " permission that's source from a non-runtime permission, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
/**
* For a legacy granted or implicit permission during the app upgrade, when the permission
- * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag
- * so that the app can request the permission.
+ * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag so
+ * that the app can request the permission.
*/
@Test
fun testEvaluatePermissionState_noLongerLegacyOrImplicitGranted_canBeRequested() {
- val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.APP_OP_REVOKED or
- PermissionFlags.RUNTIME_GRANTED
+ val oldFlags =
+ PermissionFlags.LEGACY_GRANTED or
+ PermissionFlags.APP_OP_REVOKED or
+ PermissionFlags.RUNTIME_GRANTED
testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = 0
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@Test
fun testEvaluatePermissionState_noLongerImplicit_getsRuntimeAndImplicitFlagsRemoved() {
- val oldFlags = PermissionFlags.IMPLICIT or PermissionFlags.RUNTIME_GRANTED or
- PermissionFlags.USER_SET or PermissionFlags.USER_FIXED
+ val oldFlags =
+ PermissionFlags.IMPLICIT or
+ PermissionFlags.RUNTIME_GRANTED or
+ PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED
testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = 0
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " that is no longer implicit and we shouldn't retain as nearby device" +
- " permissions, the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " that is no longer implicit and we shouldn't retain as nearby device" +
+ " permissions, the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -504,48 +514,45 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
permissionName = PERMISSION_BLUETOOTH_CONNECT,
- requestedPermissions = setOf(
- PERMISSION_BLUETOOTH_CONNECT,
- PERMISSION_ACCESS_BACKGROUND_LOCATION
- )
+ requestedPermissions =
+ setOf(PERMISSION_BLUETOOTH_CONNECT, PERMISSION_ACCESS_BACKGROUND_LOCATION),
) {
setPermissionFlags(
APP_ID_1,
getUserIdEvaluated(),
PERMISSION_ACCESS_BACKGROUND_LOCATION,
- PermissionFlags.RUNTIME_GRANTED
+ PermissionFlags.RUNTIME_GRANTED,
)
}
- val actualFlags = getPermissionFlags(
- APP_ID_1,
- getUserIdEvaluated(),
- PERMISSION_BLUETOOTH_CONNECT
- )
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BLUETOOTH_CONNECT)
val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a runtime nearby device" +
- " permission that was granted by implicit, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime nearby device" +
+ " permission that was granted by implicit, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@Test
fun testEvaluatePermissionState_noLongerImplicitSystemOrPolicyFixedWasGranted_runtimeGranted() {
- val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT or
- PermissionFlags.SYSTEM_FIXED
+ val oldFlags =
+ PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.IMPLICIT or
+ PermissionFlags.SYSTEM_FIXED
testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.SYSTEM_FIXED
assertWithMessage(
- "After $action is called for a package that requests a runtime permission" +
- " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime permission" +
+ " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -556,16 +563,16 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
testEvaluatePermissionState(
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
- permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
+ permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED,
) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = oldFlags
assertWithMessage(
- "After $action is called for a package that requests a runtime hard" +
- " restricted permission that is not exempted, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime hard" +
+ " restricted permission that is not exempted, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -576,16 +583,16 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
testEvaluatePermissionState(
oldFlags,
PermissionInfo.PROTECTION_DANGEROUS,
- permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED,
) {}
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.UPGRADE_EXEMPT
assertWithMessage(
- "After $action is called for a package that requests a runtime soft" +
- " restricted permission that is exempted, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a runtime soft" +
+ " restricted permission that is exempted, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -595,18 +602,20 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED
testInheritImplicitPermissionStates(
implicitPermissionFlags = oldImplicitPermissionFlags,
- isNewInstallAndNewPermission = false
+ isNewInstallAndNewPermission = false,
)
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
- val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or
- PermissionFlags.APP_OP_REVOKED
+ val expectedNewFlags =
+ oldImplicitPermissionFlags or
+ PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.APP_OP_REVOKED
assertWithMessage(
- "After $action is called for a package that requests a permission that is" +
- " implicit, existing and runtime, it should not inherit the runtime flags from" +
- " the source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, existing and runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -620,11 +629,11 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a permission that is" +
- " implicit, new and non-runtime, it should not inherit the runtime flags from" +
- " the source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, new and non-runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -635,14 +644,14 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags)
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
- val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or
- PermissionFlags.IMPLICIT
+ val expectedNewFlags =
+ sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
assertWithMessage(
- "After $action is called for a package that requests a permission that is" +
- " implicit, new and runtime, it should inherit the runtime flags from" +
- " the source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -653,17 +662,17 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
testInheritImplicitPermissionStates(
implicitPermissionFlags = PermissionFlags.POLICY_FIXED,
sourceRuntimeFlags = sourceRuntimeFlags,
- isAnySourcePermissionNonRuntime = false
+ isAnySourcePermissionNonRuntime = false,
)
val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT
assertWithMessage(
- "After $action is called for a package that requests a permission that is" +
- " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
- " from source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
+ " from source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -678,21 +687,18 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
testInheritImplicitPermissionStates(
implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION,
- sourceRuntimeFlags = sourceRuntimeFlags
+ sourceRuntimeFlags = sourceRuntimeFlags,
)
- val actualFlags = getPermissionFlags(
- APP_ID_1,
- getUserIdEvaluated(),
- PERMISSION_ACCESS_MEDIA_LOCATION
- )
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_ACCESS_MEDIA_LOCATION)
val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED
assertWithMessage(
- "After $action is called for a package that requests a media permission that" +
- " is implicit, new and runtime, it should inherit the runtime flags from" +
- " the source permission and have the IMPLICIT flag removed. Hence the actual" +
- " permission flags $actualFlags should match the expected flags $expectedNewFlags"
- )
+ "After $action is called for a package that requests a media permission that" +
+ " is implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission and have the IMPLICIT flag removed. Hence the actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
.that(actualFlags)
.isEqualTo(expectedNewFlags)
}
@@ -703,57 +709,65 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS,
sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET,
isAnySourcePermissionNonRuntime: Boolean = true,
- isNewInstallAndNewPermission: Boolean = true
+ isNewInstallAndNewPermission: Boolean = true,
) {
val userId = getUserIdEvaluated()
- val implicitPermission = mockParsedPermission(
- implicitPermissionName,
- PACKAGE_NAME_0,
- protectionLevel = implicitPermissionProtectionLevel,
- )
+ val implicitPermission =
+ mockParsedPermission(
+ implicitPermissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = implicitPermissionProtectionLevel,
+ )
// For source from non-runtime in order to grant by implicit
- val sourcePermission1 = mockParsedPermission(
- PERMISSION_NAME_1,
- PACKAGE_NAME_0,
- protectionLevel = if (isAnySourcePermissionNonRuntime) {
- PermissionInfo.PROTECTION_NORMAL
- } else {
- PermissionInfo.PROTECTION_DANGEROUS
- }
- )
+ val sourcePermission1 =
+ mockParsedPermission(
+ PERMISSION_NAME_1,
+ PACKAGE_NAME_0,
+ protectionLevel =
+ if (isAnySourcePermissionNonRuntime) {
+ PermissionInfo.PROTECTION_NORMAL
+ } else {
+ PermissionInfo.PROTECTION_DANGEROUS
+ },
+ )
// For inheriting runtime flags
- val sourcePermission2 = mockParsedPermission(
- PERMISSION_NAME_2,
- PACKAGE_NAME_0,
- protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
- )
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
+ val sourcePermission2 =
+ mockParsedPermission(
+ PERMISSION_NAME_2,
PACKAGE_NAME_0,
- permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2)
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
)
- )
- val installedPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(
- PACKAGE_NAME_1,
- requestedPermissions = setOf(
- implicitPermissionName,
- PERMISSION_NAME_1,
- PERMISSION_NAME_2
+ val permissionOwnerPackageState =
+ mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2),
),
- implicitPermissions = setOf(implicitPermissionName)
)
- )
- oldState.mutateExternalState().setImplicitToSourcePermissions(
- MutableIndexedMap<String, IndexedListSet<String>>().apply {
- put(implicitPermissionName, MutableIndexedListSet<String>().apply {
- add(PERMISSION_NAME_1)
- add(PERMISSION_NAME_2)
- })
- }
- )
+ val installedPackageState =
+ mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions =
+ setOf(implicitPermissionName, PERMISSION_NAME_1, PERMISSION_NAME_2),
+ implicitPermissions = setOf(implicitPermissionName),
+ ),
+ )
+ oldState
+ .mutateExternalState()
+ .setImplicitToSourcePermissions(
+ MutableIndexedMap<String, IndexedListSet<String>>().apply {
+ put(
+ implicitPermissionName,
+ MutableIndexedListSet<String>().apply {
+ add(PERMISSION_NAME_1)
+ add(PERMISSION_NAME_2)
+ },
+ )
+ }
+ )
addPackageState(permissionOwnerPackageState)
addPermission(implicitPermission)
addPermission(sourcePermission1)
@@ -772,7 +786,7 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
userId,
implicitPermissionName,
implicitPermissionFlags,
- newState
+ newState,
)
}
testAction(installedPackageState)
@@ -781,18 +795,17 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
/**
* Setup simple package states for testing evaluatePermissionState().
- * permissionOwnerPackageState is definer of permissionName with APP_ID_0.
- * installedPackageState is the installed package that requests permissionName with APP_ID_1.
+ * permissionOwnerPackageState is definer of permissionName with APP_ID_0. installedPackageState
+ * is the installed package that requests permissionName with APP_ID_1.
*
* @param oldFlags the existing permission flags for APP_ID_1, userId, permissionName
* @param protectionLevel the protectionLevel for the permission
* @param permissionName the name of the permission (1) being defined (2) of the oldFlags, and
- * (3) requested by installedPackageState
+ * (3) requested by installedPackageState
* @param requestedPermissions the permissions requested by installedPackageState
* @param implicitPermissions the implicit permissions of installedPackageState
* @param permissionInfoFlags the flags for the permission itself
* @param isInstalledPackageSystem whether installedPackageState is a system package
- *
* @return installedPackageState
*/
private fun testEvaluatePermissionState(
@@ -809,33 +822,36 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
isInstalledPackageVendor: Boolean = false,
installedPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
isNewInstall: Boolean = false,
- additionalSetup: () -> Unit
+ additionalSetup: () -> Unit,
) {
val userId = getUserIdEvaluated()
- val parsedPermission = mockParsedPermission(
- permissionName,
- PACKAGE_NAME_0,
- protectionLevel = protectionLevel,
- flags = permissionInfoFlags
- )
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
- )
- val installedPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(
- PACKAGE_NAME_1,
- requestedPermissions = requestedPermissions,
- implicitPermissions = implicitPermissions,
- targetSdkVersion = installedPackageTargetSdkVersion,
- isSignatureMatching = isInstalledPackageSignatureMatching
- ),
- isSystem = isInstalledPackageSystem,
- isPrivileged = isInstalledPackagePrivileged,
- isProduct = isInstalledPackageProduct,
- isVendor = isInstalledPackageVendor
- )
+ val parsedPermission =
+ mockParsedPermission(
+ permissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = protectionLevel,
+ flags = permissionInfoFlags,
+ )
+ val permissionOwnerPackageState =
+ mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission)),
+ )
+ val installedPackageState =
+ mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = requestedPermissions,
+ implicitPermissions = implicitPermissions,
+ targetSdkVersion = installedPackageTargetSdkVersion,
+ isSignatureMatching = isInstalledPackageSignatureMatching,
+ ),
+ isSystem = isInstalledPackageSystem,
+ isPrivileged = isInstalledPackagePrivileged,
+ isProduct = isInstalledPackageProduct,
+ isVendor = isInstalledPackageVendor,
+ )
addPackageState(permissionOwnerPackageState)
if (!isNewInstall) {
addPackageState(installedPackageState)
@@ -854,26 +870,29 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
}
}
- private fun getUserIdEvaluated(): Int = when (action) {
- Action.ON_USER_ADDED -> USER_ID_NEW
- Action.ON_STORAGE_VOLUME_ADDED, Action.ON_PACKAGE_ADDED -> USER_ID_0
- }
+ private fun getUserIdEvaluated(): Int =
+ when (action) {
+ Action.ON_USER_ADDED -> USER_ID_NEW
+ Action.ON_STORAGE_VOLUME_ADDED,
+ Action.ON_PACKAGE_ADDED -> USER_ID_0
+ }
private fun MutateStateScope.testAction(packageState: PackageState) {
with(appIdPermissionPolicy) {
when (action) {
Action.ON_USER_ADDED -> onUserAdded(getUserIdEvaluated())
- Action.ON_STORAGE_VOLUME_ADDED -> onStorageVolumeMounted(
- null,
- listOf(packageState.packageName),
- true
- )
+ Action.ON_STORAGE_VOLUME_ADDED ->
+ onStorageVolumeMounted(null, listOf(packageState.packageName), true)
Action.ON_PACKAGE_ADDED -> onPackageAdded(packageState)
}
}
}
- enum class Action { ON_USER_ADDED, ON_STORAGE_VOLUME_ADDED, ON_PACKAGE_ADDED }
+ enum class Action {
+ ON_USER_ADDED,
+ ON_STORAGE_VOLUME_ADDED,
+ ON_PACKAGE_ADDED,
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
index 7b3f21603c0a..207820cc3135 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
@@ -49,32 +49,24 @@ import org.junit.Rule
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyLong
-/**
- * Mocking unit test for AppIdPermissionPolicy.
- */
+/** Mocking unit test for AppIdPermissionPolicy. */
@RunWith(AndroidJUnit4::class)
abstract class BasePermissionPolicyTest {
protected lateinit var oldState: MutableAccessState
protected lateinit var newState: MutableAccessState
- protected val defaultPermissionGroup = mockParsedPermissionGroup(
- PERMISSION_GROUP_NAME_0,
- PACKAGE_NAME_0
- )
- protected val defaultPermissionTree = mockParsedPermission(
- PERMISSION_TREE_NAME,
- PACKAGE_NAME_0,
- isTree = true
- )
+ protected val defaultPermissionGroup =
+ mockParsedPermissionGroup(PERMISSION_GROUP_NAME_0, PACKAGE_NAME_0)
+ protected val defaultPermissionTree =
+ mockParsedPermission(PERMISSION_TREE_NAME, PACKAGE_NAME_0, isTree = true)
protected val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
protected val appIdPermissionPolicy = AppIdPermissionPolicy()
@Rule
@JvmField
- val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
- .spyStatic(PackageInfoUtils::class.java)
- .build()
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).spyStatic(PackageInfoUtils::class.java).build()
@Before
fun baseSetUp() {
@@ -93,65 +85,76 @@ abstract class BasePermissionPolicyTest {
private fun mockPackageInfoUtilsGeneratePermissionInfo() {
wheneverStatic {
- PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong())
- }.thenAnswer { invocation ->
- val parsedPermission = invocation.getArgument<ParsedPermission>(0)
- val generateFlags = invocation.getArgument<Long>(1)
- PermissionInfo(parsedPermission.backgroundPermission).apply {
- name = parsedPermission.name
- packageName = parsedPermission.packageName
- metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
- parsedPermission.metaData
- } else {
- null
+ PackageInfoUtils.generatePermissionInfo(
+ any(ParsedPermission::class.java),
+ anyLong(),
+ )
+ }
+ .thenAnswer { invocation ->
+ val parsedPermission = invocation.getArgument<ParsedPermission>(0)
+ val generateFlags = invocation.getArgument<Long>(1)
+ PermissionInfo(parsedPermission.backgroundPermission).apply {
+ name = parsedPermission.name
+ packageName = parsedPermission.packageName
+ metaData =
+ if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
+ parsedPermission.metaData
+ } else {
+ null
+ }
+ @Suppress("DEPRECATION")
+ protectionLevel = parsedPermission.protectionLevel
+ group = parsedPermission.group
+ flags = parsedPermission.flags
}
- @Suppress("DEPRECATION")
- protectionLevel = parsedPermission.protectionLevel
- group = parsedPermission.group
- flags = parsedPermission.flags
}
- }
}
private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() {
wheneverStatic {
- PackageInfoUtils.generatePermissionGroupInfo(
- any(ParsedPermissionGroup::class.java),
- anyLong()
- )
- }.thenAnswer { invocation ->
- val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0)
- val generateFlags = invocation.getArgument<Long>(1)
- @Suppress("DEPRECATION")
- PermissionGroupInfo().apply {
- name = parsedPermissionGroup.name
- packageName = parsedPermissionGroup.packageName
- metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
- parsedPermissionGroup.metaData
- } else {
- null
+ PackageInfoUtils.generatePermissionGroupInfo(
+ any(ParsedPermissionGroup::class.java),
+ anyLong(),
+ )
+ }
+ .thenAnswer { invocation ->
+ val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0)
+ val generateFlags = invocation.getArgument<Long>(1)
+ @Suppress("DEPRECATION")
+ PermissionGroupInfo().apply {
+ name = parsedPermissionGroup.name
+ packageName = parsedPermissionGroup.packageName
+ metaData =
+ if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
+ parsedPermissionGroup.metaData
+ } else {
+ null
+ }
+ flags = parsedPermissionGroup.flags
}
- flags = parsedPermissionGroup.flags
}
- }
}
- /**
- * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
- */
+ /** Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0 */
protected fun mockSimpleAndroidPackage(): AndroidPackage =
mockAndroidPackage(
PACKAGE_NAME_0,
permissionGroups = listOf(defaultPermissionGroup),
- permissions = listOf(defaultPermissionTree, defaultPermission)
+ permissions = listOf(defaultPermissionTree, defaultPermission),
)
protected fun createSimplePermission(isTree: Boolean = false): Permission {
- val parsedPermission = if (isTree) { defaultPermissionTree } else { defaultPermission }
- val permissionInfo = PackageInfoUtils.generatePermissionInfo(
- parsedPermission,
- PackageManager.GET_META_DATA.toLong()
- )!!
+ val parsedPermission =
+ if (isTree) {
+ defaultPermissionTree
+ } else {
+ defaultPermission
+ }
+ val permissionInfo =
+ PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong(),
+ )!!
return Permission(permissionInfo, true, Permission.TYPE_MANIFEST, APP_ID_0)
}
@@ -164,13 +167,12 @@ abstract class BasePermissionPolicyTest {
appId: Int,
packageName: String,
isSystem: Boolean = false,
- ): PackageState =
- mock {
- whenever(this.appId).thenReturn(appId)
- whenever(this.packageName).thenReturn(packageName)
- whenever(androidPackage).thenReturn(null)
- whenever(this.isSystem).thenReturn(isSystem)
- }
+ ): PackageState = mock {
+ whenever(this.appId).thenReturn(appId)
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(androidPackage).thenReturn(null)
+ whenever(this.isSystem).thenReturn(isSystem)
+ }
protected fun mockPackageState(
appId: Int,
@@ -179,22 +181,22 @@ abstract class BasePermissionPolicyTest {
isPrivileged: Boolean = false,
isProduct: Boolean = false,
isInstantApp: Boolean = false,
- isVendor: Boolean = false
- ): PackageState =
- mock {
- whenever(this.appId).thenReturn(appId)
- whenever(this.androidPackage).thenReturn(androidPackage)
- val packageName = androidPackage.packageName
- whenever(this.packageName).thenReturn(packageName)
- whenever(this.isSystem).thenReturn(isSystem)
- whenever(this.isPrivileged).thenReturn(isPrivileged)
- whenever(this.isProduct).thenReturn(isProduct)
- whenever(this.isVendor).thenReturn(isVendor)
- val userStates = SparseArray<PackageUserState>().apply {
+ isVendor: Boolean = false,
+ ): PackageState = mock {
+ whenever(this.appId).thenReturn(appId)
+ whenever(this.androidPackage).thenReturn(androidPackage)
+ val packageName = androidPackage.packageName
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(this.isSystem).thenReturn(isSystem)
+ whenever(this.isPrivileged).thenReturn(isPrivileged)
+ whenever(this.isProduct).thenReturn(isProduct)
+ whenever(this.isVendor).thenReturn(isVendor)
+ val userStates =
+ SparseArray<PackageUserState>().apply {
put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) })
}
- whenever(this.userStates).thenReturn(userStates)
- }
+ whenever(this.userStates).thenReturn(userStates)
+ }
protected fun mockAndroidPackage(
packageName: String,
@@ -205,28 +207,26 @@ abstract class BasePermissionPolicyTest {
requestedPermissions: Set<String> = emptySet(),
permissionGroups: List<ParsedPermissionGroup> = emptyList(),
permissions: List<ParsedPermission> = emptyList(),
- isSignatureMatching: Boolean = false
- ): AndroidPackage =
- mock {
- whenever(this.packageName).thenReturn(packageName)
- whenever(this.targetSdkVersion).thenReturn(targetSdkVersion)
- whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage)
- whenever(this.adoptPermissions).thenReturn(adoptPermissions)
- whenever(this.implicitPermissions).thenReturn(implicitPermissions)
- whenever(this.requestedPermissions).thenReturn(requestedPermissions)
- whenever(this.permissionGroups).thenReturn(permissionGroups)
- whenever(this.permissions).thenReturn(permissions)
- val signingDetails = mock<SigningDetails> {
- whenever(
- hasCommonSignerWithCapability(any(), any())
- ).thenReturn(isSignatureMatching)
+ isSignatureMatching: Boolean = false,
+ ): AndroidPackage = mock {
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(this.targetSdkVersion).thenReturn(targetSdkVersion)
+ whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage)
+ whenever(this.adoptPermissions).thenReturn(adoptPermissions)
+ whenever(this.implicitPermissions).thenReturn(implicitPermissions)
+ whenever(this.requestedPermissions).thenReturn(requestedPermissions)
+ whenever(this.permissionGroups).thenReturn(permissionGroups)
+ whenever(this.permissions).thenReturn(permissions)
+ val signingDetails =
+ mock<SigningDetails> {
+ whenever(hasCommonSignerWithCapability(any(), any()))
+ .thenReturn(isSignatureMatching)
whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching)
- whenever(
- checkCapability(any<SigningDetails>(), any())
- ).thenReturn(isSignatureMatching)
+ whenever(checkCapability(any<SigningDetails>(), any()))
+ .thenReturn(isSignatureMatching)
}
- whenever(this.signingDetails).thenReturn(signingDetails)
- }
+ whenever(this.signingDetails).thenReturn(signingDetails)
+ }
protected fun mockParsedPermission(
permissionName: String,
@@ -235,72 +235,74 @@ abstract class BasePermissionPolicyTest {
group: String? = null,
protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL,
flags: Int = 0,
- isTree: Boolean = false
- ): ParsedPermission =
- mock {
- whenever(name).thenReturn(permissionName)
- whenever(this.packageName).thenReturn(packageName)
- whenever(metaData).thenReturn(Bundle())
- whenever(this.backgroundPermission).thenReturn(backgroundPermission)
- whenever(this.group).thenReturn(group)
- whenever(this.protectionLevel).thenReturn(protectionLevel)
- whenever(this.flags).thenReturn(flags)
- whenever(this.isTree).thenReturn(isTree)
- }
+ isTree: Boolean = false,
+ ): ParsedPermission = mock {
+ whenever(name).thenReturn(permissionName)
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(metaData).thenReturn(Bundle())
+ whenever(this.backgroundPermission).thenReturn(backgroundPermission)
+ whenever(this.group).thenReturn(group)
+ whenever(this.protectionLevel).thenReturn(protectionLevel)
+ whenever(this.flags).thenReturn(flags)
+ whenever(this.isTree).thenReturn(isTree)
+ }
protected fun mockParsedPermissionGroup(
permissionGroupName: String,
packageName: String,
- ): ParsedPermissionGroup =
- mock {
- whenever(name).thenReturn(permissionGroupName)
- whenever(this.packageName).thenReturn(packageName)
- whenever(metaData).thenReturn(Bundle())
- }
+ ): ParsedPermissionGroup = mock {
+ whenever(name).thenReturn(permissionGroupName)
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(metaData).thenReturn(Bundle())
+ }
protected fun addPackageState(
packageState: PackageState,
- state: MutableAccessState = oldState
+ state: MutableAccessState = oldState,
) {
state.mutateExternalState().apply {
setPackageStates(
packageStates.toMutableMap().apply { put(packageState.packageName, packageState) }
)
- mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
+ mutateAppIdPackageNames()
+ .mutateOrPut(packageState.appId) { MutableIndexedListSet() }
.add(packageState.packageName)
}
}
protected fun removePackageState(
packageState: PackageState,
- state: MutableAccessState = oldState
+ state: MutableAccessState = oldState,
) {
state.mutateExternalState().apply {
setPackageStates(
packageStates.toMutableMap().apply { remove(packageState.packageName) }
)
- mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
+ mutateAppIdPackageNames()
+ .mutateOrPut(packageState.appId) { MutableIndexedListSet() }
.remove(packageState.packageName)
}
}
protected fun addDisabledSystemPackageState(
packageState: PackageState,
- state: MutableAccessState = oldState
- ) = state.mutateExternalState().apply {
- (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState
- }
+ state: MutableAccessState = oldState,
+ ) =
+ state.mutateExternalState().apply {
+ (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState
+ }
protected fun addPermission(
parsedPermission: ParsedPermission,
type: Int = Permission.TYPE_MANIFEST,
isReconciled: Boolean = true,
- state: MutableAccessState = oldState
+ state: MutableAccessState = oldState,
) {
- val permissionInfo = PackageInfoUtils.generatePermissionInfo(
- parsedPermission,
- PackageManager.GET_META_DATA.toLong()
- )!!
+ val permissionInfo =
+ PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong(),
+ )!!
val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId
val permission = Permission(permissionInfo, isReconciled, type, appId)
if (parsedPermission.isTree) {
@@ -312,35 +314,35 @@ abstract class BasePermissionPolicyTest {
protected fun addPermissionGroup(
parsedPermissionGroup: ParsedPermissionGroup,
- state: MutableAccessState = oldState
+ state: MutableAccessState = oldState,
) {
state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] =
PackageInfoUtils.generatePermissionGroupInfo(
parsedPermissionGroup,
- PackageManager.GET_META_DATA.toLong()
+ PackageManager.GET_META_DATA.toLong(),
)!!
}
protected fun getPermission(
permissionName: String,
- state: MutableAccessState = newState
+ state: MutableAccessState = newState,
): Permission? = state.systemState.permissions[permissionName]
protected fun getPermissionTree(
permissionTreeName: String,
- state: MutableAccessState = newState
+ state: MutableAccessState = newState,
): Permission? = state.systemState.permissionTrees[permissionTreeName]
protected fun getPermissionGroup(
permissionGroupName: String,
- state: MutableAccessState = newState
+ state: MutableAccessState = newState,
): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName]
protected fun getPermissionFlags(
appId: Int,
userId: Int,
permissionName: String,
- state: MutableAccessState = newState
+ state: MutableAccessState = newState,
): Int =
state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
@@ -349,11 +351,13 @@ abstract class BasePermissionPolicyTest {
userId: Int,
permissionName: String,
flags: Int,
- state: MutableAccessState = oldState
+ state: MutableAccessState = oldState,
) =
- state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) {
- MutableIndexedMap()
- }.put(permissionName, flags)
+ state
+ .mutateUserState(userId)!!
+ .mutateAppIdPermissionFlags()
+ .mutateOrPut(appId) { MutableIndexedMap() }
+ .put(permissionName, flags)
companion object {
@JvmStatic protected val PACKAGE_NAME_0 = "packageName0"
@@ -375,16 +379,17 @@ abstract class BasePermissionPolicyTest {
@JvmStatic protected val PERMISSION_NAME_1 = "permissionName1"
@JvmStatic protected val PERMISSION_NAME_2 = "permissionName2"
@JvmStatic protected val PERMISSION_BELONGS_TO_A_TREE = "permissionTree.permission"
- @JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE =
- Manifest.permission.READ_EXTERNAL_STORAGE
- @JvmStatic protected val PERMISSION_POST_NOTIFICATIONS =
- Manifest.permission.POST_NOTIFICATIONS
- @JvmStatic protected val PERMISSION_BLUETOOTH_CONNECT =
- Manifest.permission.BLUETOOTH_CONNECT
- @JvmStatic protected val PERMISSION_ACCESS_BACKGROUND_LOCATION =
+ @JvmStatic
+ protected val PERMISSION_READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE
+ @JvmStatic
+ protected val PERMISSION_POST_NOTIFICATIONS = Manifest.permission.POST_NOTIFICATIONS
+ @JvmStatic
+ protected val PERMISSION_BLUETOOTH_CONNECT = Manifest.permission.BLUETOOTH_CONNECT
+ @JvmStatic
+ protected val PERMISSION_ACCESS_BACKGROUND_LOCATION =
Manifest.permission.ACCESS_BACKGROUND_LOCATION
- @JvmStatic protected val PERMISSION_ACCESS_MEDIA_LOCATION =
- Manifest.permission.ACCESS_MEDIA_LOCATION
+ @JvmStatic
+ protected val PERMISSION_ACCESS_MEDIA_LOCATION = Manifest.permission.ACCESS_MEDIA_LOCATION
@JvmStatic protected val USER_ID_0 = 0
@JvmStatic protected val USER_ID_NEW = 1
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index ab3784b07e10..259ba989a021 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -34,25 +34,32 @@ import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.BackgroundStartPrivileges;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Looper;
import android.os.UserHandle;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
@@ -108,7 +115,8 @@ public class PendingIntentControllerTest {
mPendingIntentController.onActivityManagerInternalAdded();
}
- private PendingIntentRecord createPendingIntentRecord(int flags) {
+ @NonNull
+ private PendingIntentRecord createPendingIntentRecord(@PendingIntent.Flags int flags) {
return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST,
TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, TEST_USER_ID, null, null, 0,
TEST_INTENTS, null, flags, null);
@@ -219,6 +227,58 @@ public class PendingIntentControllerTest {
allowlistDurationLocked.type);
}
+ @Test
+ public void testSendWithBundleExtras() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ final ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchDisplayId(2);
+ activityOptions.setLaunchTaskId(123);
+ final Bundle options = activityOptions.toBundle();
+ options.putString("testKey", "testValue");
+
+ pir.send(0, null, null, null, null, null, options);
+
+ final ArgumentCaptor<Bundle> resultExtrasCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mActivityManagerInternal).broadcastIntentInPackage(
+ eq(TEST_PACKAGE_NAME),
+ eq(TEST_FEATURE_ID),
+ eq(TEST_CALLING_UID),
+ eq(TEST_CALLING_UID), // realCallingUid
+ anyInt(), // realCallingPid
+ any(), // intent
+ any(), // resolvedType
+ any(), // resultToThread
+ any(), // resultTo
+ anyInt(), // resultCode
+ any(), // resultData
+ resultExtrasCaptor.capture(), // resultExtras
+ any(), // requiredPermission
+ eq(options), // bOptions
+ anyBoolean(), // serialized
+ anyBoolean(), // sticky
+ anyInt(), // userId
+ any(), // backgroundStartPrivileges
+ any() // broadcastAllowList
+ );
+ final Bundle result = resultExtrasCaptor.getValue();
+ if (com.android.window.flags.Flags.supportWidgetIntentsOnConnectedDisplay()) {
+ // Check that only launchDisplayId in ActivityOptions is passed via resultExtras.
+ final ActivityOptions expected = ActivityOptions.makeBasic().setLaunchDisplayId(2);
+ assertBundleEquals(expected.toBundle(), result);
+ // Check that launchTaskId is dropped in resultExtras.
+ assertNotEquals(123, ActivityOptions.fromBundle(result).getLaunchTaskId());
+ } else {
+ assertNull(result);
+ }
+ }
+
+ private void assertBundleEquals(@NonNull Bundle expected, @NonNull Bundle observed) {
+ assertEquals(expected.size(), observed.size());
+ for (String key : expected.keySet()) {
+ assertEquals(expected.get(key), observed.get(key));
+ }
+ }
+
private void assertCancelReason(int expectedReason, int actualReason) {
final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
+ "; Actual: " + cancelReasonToString(actualReason);
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 6b8ef88c556c..59eb0c890af5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -94,6 +94,7 @@ import com.android.internal.R;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DesktopModeHelper;
import com.android.server.wm.WindowManagerInternal;
@@ -169,6 +170,8 @@ public class WallpaperManagerServiceTests {
private static WindowManagerInternal sWindowManagerInternal;
+ private static ActivityTaskManagerInternal sActivityTaskManagerInternal;
+
@BeforeClass
public static void setUpClass() {
sMockitoSession = mockitoSession()
@@ -181,6 +184,9 @@ public class WallpaperManagerServiceTests {
sWindowManagerInternal = mock(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, sWindowManagerInternal);
+ sActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class,
+ sActivityTaskManagerInternal);
sContext.addMockSystemService(Context.APP_OPS_SERVICE, mock(AppOpsManager.class));
@@ -232,6 +238,7 @@ public class WallpaperManagerServiceTests {
sMockitoSession = null;
}
LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
sImageWallpaperComponentName = null;
sDefaultWallpaperComponent = null;
sFallbackWallpaperComponentName = null;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 7acb93c0641e..3f937331b52e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -67,10 +67,14 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
/**
* Test BatteryStatsHistory.
@@ -141,7 +145,7 @@ public class BatteryStatsHistoryTest {
mEventLogger);
mHistory.forceRecordAllHistory();
mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false);
- mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"));
+ mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"), 0);
}
@Test
@@ -865,6 +869,39 @@ public class BatteryStatsHistoryTest {
return events;
}
+
+ @Test
+ public void historyLogTimeFormatter() {
+ historyLogTimeFormatting("GMT");
+ historyLogTimeFormatting("PST");
+ historyLogTimeFormatting("NST"); // UTC−03:30
+ historyLogTimeFormatting("NPT"); // UTC+05:45
+ }
+
+ private void historyLogTimeFormatting(String timeZoneId) {
+ TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
+ BatteryStats.HistoryPrinter printer = new BatteryStats.HistoryPrinter(timeZone, 0);
+ SimpleDateFormat simpleDateFormat =
+ new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
+ simpleDateFormat.getCalendar().setTimeZone(timeZone);
+ Date date = new Date();
+
+ HistoryItem item = new HistoryItem();
+ long base = 1738746000000L;
+ for (long offset = 1; offset < TimeUnit.DAYS.toMillis(365 * 100); offset *= 7) {
+ item.currentTime = base + offset;
+ date.setTime(item.currentTime);
+ String expected = simpleDateFormat.format(date);
+ StringWriter sw = new StringWriter();
+ PrintWriter writer = new PrintWriter(sw);
+ printer.printNextItem(writer, item, 0, false, false);
+ writer.flush();
+ String actual = sw.toString().trim().substring(0, "MM-dd HH:mm:ss.SSS".length());
+
+ assertThat(actual).isEqualTo(expected);
+ }
+ }
+
private static void awaitCompletion() {
ConditionVariable done = new ConditionVariable();
BackgroundThread.getHandler().post(done::open);
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index d6b3fecb487c..ca71ed6a4130 100644
--- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
import android.os.test.TestLooper;
@@ -36,6 +37,7 @@ import android.security.advancedprotection.IAdvancedProtectionCallback;
import androidx.annotation.NonNull;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
@@ -48,26 +50,37 @@ import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
@SuppressLint("VisibleForTests")
@RunWith(JUnit4.class)
public class AdvancedProtectionServiceTest {
- private AdvancedProtectionService mService;
private FakePermissionEnforcer mPermissionEnforcer;
+ private UserManagerInternal mUserManager;
private Context mContext;
- private AdvancedProtectionService.AdvancedProtectionStore mStore;
private TestLooper mLooper;
- AdvancedProtectionFeature mTestFeature2g = new AdvancedProtectionFeature(
- AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
+ AdvancedProtectionFeature mTestFeature2g =
+ new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
@Before
public void setup() throws Settings.SettingNotFoundException {
mContext = mock(Context.class);
+ mUserManager = mock(UserManagerInternal.class);
+ mLooper = new TestLooper();
mPermissionEnforcer = new FakePermissionEnforcer();
mPermissionEnforcer.grant(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE);
mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
+ Mockito.when(mUserManager.getUserInfo(ArgumentMatchers.anyInt()))
+ .thenReturn(new UserInfo(0, "user", UserInfo.FLAG_ADMIN));
+ }
+
+ private AdvancedProtectionService createService(
+ AdvancedProtectionHook hook, AdvancedProtectionProvider provider) {
- mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) {
+ AdvancedProtectionService.AdvancedProtectionStore
+ store = new AdvancedProtectionService.AdvancedProtectionStore(mContext) {
private Map<String, Integer> mStoredValues = new HashMap<>();
private boolean mEnabled = false;
@@ -92,24 +105,38 @@ public class AdvancedProtectionServiceTest {
}
};
- mLooper = new TestLooper();
-
- mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, null, null);
+ return new AdvancedProtectionService(
+ mContext,
+ store,
+ mUserManager,
+ mLooper.getLooper(),
+ mPermissionEnforcer,
+ hook,
+ provider);
}
@Test
public void testToggleProtection() {
- mService.setAdvancedProtectionEnabled(true);
- assertTrue(mService.isAdvancedProtectionEnabled());
+ AdvancedProtectionService service = createService(null, null);
+ service.setAdvancedProtectionEnabled(true);
+ assertTrue(service.isAdvancedProtectionEnabled());
- mService.setAdvancedProtectionEnabled(false);
- assertFalse(mService.isAdvancedProtectionEnabled());
+ service.setAdvancedProtectionEnabled(false);
+ assertFalse(service.isAdvancedProtectionEnabled());
}
@Test
public void testDisableProtection_byDefault() {
- assertFalse(mService.isAdvancedProtectionEnabled());
+ AdvancedProtectionService service = createService(null, null);
+ assertFalse(service.isAdvancedProtectionEnabled());
+ }
+
+ @Test
+ public void testSetProtection_nonAdminUser() {
+ Mockito.when(mUserManager.getUserInfo(ArgumentMatchers.anyInt()))
+ .thenReturn(new UserInfo(1, "user2", UserInfo.FLAG_FULL));
+ AdvancedProtectionService service = createService(null, null);
+ assertThrows(SecurityException.class, () -> service.setAdvancedProtectionEnabled(true));
}
@Test
@@ -134,9 +161,8 @@ public class AdvancedProtectionServiceTest {
}
};
- mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook, null);
- mService.setAdvancedProtectionEnabled(true);
+ AdvancedProtectionService service = createService(hook, null);
+ service.setAdvancedProtectionEnabled(true);
mLooper.dispatchNext();
assertTrue(callbackCaptor.get());
@@ -164,10 +190,8 @@ public class AdvancedProtectionServiceTest {
}
};
- mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook, null);
-
- mService.setAdvancedProtectionEnabled(true);
+ AdvancedProtectionService service = createService(hook, null);
+ service.setAdvancedProtectionEnabled(true);
mLooper.dispatchNext();
assertFalse(callbackCalledCaptor.get());
}
@@ -194,14 +218,13 @@ public class AdvancedProtectionServiceTest {
}
};
- mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook, null);
- mService.setAdvancedProtectionEnabled(true);
+ AdvancedProtectionService service = createService(hook, null);
+ service.setAdvancedProtectionEnabled(true);
mLooper.dispatchNext();
assertTrue(callbackCalledCaptor.get());
callbackCalledCaptor.set(false);
- mService.setAdvancedProtectionEnabled(true);
+ service.setAdvancedProtectionEnabled(true);
mLooper.dispatchAll();
assertFalse(callbackCalledCaptor.get());
}
@@ -209,21 +232,24 @@ public class AdvancedProtectionServiceTest {
@Test
public void testRegisterCallback() throws RemoteException {
AtomicBoolean callbackCaptor = new AtomicBoolean(false);
- IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() {
- @Override
- public void onAdvancedProtectionChanged(boolean enabled) {
- callbackCaptor.set(enabled);
- }
- };
+ IAdvancedProtectionCallback callback =
+ new IAdvancedProtectionCallback.Stub() {
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
+ callbackCaptor.set(enabled);
+ }
+ };
+
+ AdvancedProtectionService service = createService(null, null);
- mService.setAdvancedProtectionEnabled(true);
+ service.setAdvancedProtectionEnabled(true);
mLooper.dispatchAll();
- mService.registerAdvancedProtectionCallback(callback);
+ service.registerAdvancedProtectionCallback(callback);
mLooper.dispatchNext();
assertTrue(callbackCaptor.get());
- mService.setAdvancedProtectionEnabled(false);
+ service.setAdvancedProtectionEnabled(false);
mLooper.dispatchNext();
assertFalse(callbackCaptor.get());
@@ -232,20 +258,23 @@ public class AdvancedProtectionServiceTest {
@Test
public void testUnregisterCallback() throws RemoteException {
AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false);
- IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() {
- @Override
- public void onAdvancedProtectionChanged(boolean enabled) {
- callbackCalledCaptor.set(true);
- }
- };
+ IAdvancedProtectionCallback callback =
+ new IAdvancedProtectionCallback.Stub() {
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
+ callbackCalledCaptor.set(true);
+ }
+ };
+
+ AdvancedProtectionService service = createService(null, null);
- mService.setAdvancedProtectionEnabled(true);
- mService.registerAdvancedProtectionCallback(callback);
+ service.setAdvancedProtectionEnabled(true);
+ service.registerAdvancedProtectionCallback(callback);
mLooper.dispatchAll();
callbackCalledCaptor.set(false);
- mService.unregisterAdvancedProtectionCallback(callback);
- mService.setAdvancedProtectionEnabled(false);
+ service.unregisterAdvancedProtectionCallback(callback);
+ service.setAdvancedProtectionEnabled(false);
mLooper.dispatchNext();
assertFalse(callbackCalledCaptor.get());
@@ -253,104 +282,107 @@ public class AdvancedProtectionServiceTest {
@Test
public void testGetFeatures() {
- AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature(
- AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
- AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature(
- AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
- AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
- @NonNull
- @Override
- public AdvancedProtectionFeature getFeature() {
- return feature1;
- }
+ AdvancedProtectionFeature feature1 =
+ new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
+ AdvancedProtectionFeature feature2 =
+ new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ AdvancedProtectionHook hook =
+ new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return feature1;
+ }
- @Override
- public boolean isAvailable() {
- return true;
- }
- };
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+ };
- AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
- @Override
- public List<AdvancedProtectionFeature> getFeatures(Context context) {
- return List.of(feature2);
- }
- };
+ AdvancedProtectionProvider provider =
+ new AdvancedProtectionProvider() {
+ @Override
+ public List<AdvancedProtectionFeature> getFeatures(Context context) {
+ return List.of(feature2);
+ }
+ };
- mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook, provider);
- List<AdvancedProtectionFeature> features = mService.getAdvancedProtectionFeatures();
+ AdvancedProtectionService service = createService(hook, provider);
+ List<AdvancedProtectionFeature> features = service.getAdvancedProtectionFeatures();
assertThat(features, containsInAnyOrder(feature1, feature2));
}
@Test
public void testGetFeatures_featureNotAvailable() {
- AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature(
- AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
- AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature(
- AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
- AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
- @NonNull
- @Override
- public AdvancedProtectionFeature getFeature() {
- return feature1;
- }
+ AdvancedProtectionFeature feature1 =
+ new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
+ AdvancedProtectionFeature feature2 =
+ new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ AdvancedProtectionHook hook =
+ new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return feature1;
+ }
- @Override
- public boolean isAvailable() {
- return false;
- }
- };
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+ };
- AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
- @Override
- public List<AdvancedProtectionFeature> getFeatures(Context context) {
- return List.of(feature2);
- }
- };
+ AdvancedProtectionProvider provider =
+ new AdvancedProtectionProvider() {
+ @Override
+ public List<AdvancedProtectionFeature> getFeatures(Context context) {
+ return List.of(feature2);
+ }
+ };
- mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook, provider);
- List<AdvancedProtectionFeature> features = mService.getAdvancedProtectionFeatures();
+ AdvancedProtectionService service = createService(hook, provider);
+ List<AdvancedProtectionFeature> features = service.getAdvancedProtectionFeatures();
assertThat(features, containsInAnyOrder(feature2));
}
-
@Test
public void testSetProtection_withoutPermission() {
+ AdvancedProtectionService service = createService(null, null);
mPermissionEnforcer.revoke(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE);
- assertThrows(SecurityException.class, () -> mService.setAdvancedProtectionEnabled(true));
+ assertThrows(SecurityException.class, () -> service.setAdvancedProtectionEnabled(true));
}
@Test
public void testGetProtection_withoutPermission() {
+ AdvancedProtectionService service = createService(null, null);
mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
- assertThrows(SecurityException.class, () -> mService.isAdvancedProtectionEnabled());
- }
-
- @Test
- public void testUsbDataProtection_withoutPermission() {
- mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
- assertThrows(SecurityException.class, () -> mService.isUsbDataProtectionEnabled());
- }
-
- @Test
- public void testSetUsbDataProtection_withoutPermission() {
- mPermissionEnforcer.revoke(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE);
- assertThrows(SecurityException.class, () -> mService.setUsbDataProtectionEnabled(true));
+ assertThrows(SecurityException.class, () -> service.isAdvancedProtectionEnabled());
}
@Test
public void testRegisterCallback_withoutPermission() {
+ AdvancedProtectionService service = createService(null, null);
mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
- assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback(
- new IAdvancedProtectionCallback.Default()));
+ assertThrows(
+ SecurityException.class,
+ () ->
+ service.registerAdvancedProtectionCallback(
+ new IAdvancedProtectionCallback.Default()));
}
@Test
public void testUnregisterCallback_withoutPermission() {
+ AdvancedProtectionService service = createService(null, null);
mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
- assertThrows(SecurityException.class, () -> mService.unregisterAdvancedProtectionCallback(
- new IAdvancedProtectionCallback.Default()));
+ assertThrows(
+ SecurityException.class,
+ () ->
+ service.unregisterAdvancedProtectionCallback(
+ new IAdvancedProtectionCallback.Default()));
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 159b3fd7b5c3..b9b4a78df0ca 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -17682,6 +17682,58 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({android.app.Flags.FLAG_API_RICH_ONGOING,
+ android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testApplyAdjustment_promotedOngoingNotification_doesNotApply() throws Exception {
+ // promoted ongoing notification which should not have the adjustment applied
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+ .setColor(Color.WHITE)
+ .setColorized(true)
+ .setOngoing(true)
+ .setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
+ .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+ .build();
+
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ final NotificationRecord r = new NotificationRecord(mContext, sbn,
+ mTestNotificationChannel);
+
+ // regular notification record for contrast
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel);
+
+ mService.addNotification(r);
+ mService.addNotification(r2);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(KEY_TYPE, TYPE_NEWS);
+ Bundle signals2 = new Bundle(signals); // copy for the second adjustment
+ Adjustment adjustment = new Adjustment(
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ Adjustment a2 = new Adjustment(r2.getSbn().getPackageName(), r2.getKey(), signals2, "",
+ r2.getUser().getIdentifier());
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ mBinderService.applyAdjustmentsFromAssistant(null, List.of(adjustment, a2));
+
+ waitForIdle();
+
+ r.applyAdjustments();
+ r2.applyAdjustments();
+
+ // promoted ongoing notification does not get bundled; regular one does
+ assertThat(r.getChannel().getId()).isEqualTo(mTestNotificationChannel.getId());
+ assertThat(r2.getChannel().getId()).isEqualTo(NEWS_ID);
+ }
+
+ @Test
@EnableFlags({android.app.Flags.FLAG_API_RICH_ONGOING})
public void testSetCanBePromoted_granted_noui() throws Exception {
testSetCanBePromoted_granted();
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index e9da53a8a899..5097231b468f 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -352,7 +352,53 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
}
@Override
- public StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId,
+ public StorageStats queryArtManagedStats(String packageName, int userId, int uid) {
+ if (userId != UserHandle.getCallingUserId()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
+ }
+
+ ApplicationInfo appInfo;
+ if (!TextUtils.isEmpty(packageName)) {
+ try {
+ appInfo = mPackage.getApplicationInfoAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ } catch (NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+ uid = appInfo.uid;
+ if (defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length > 1) {
+ // Multiple packages, skip
+ return translate(new PackageStats(TAG));
+ }
+ }
+
+ int callingUid = Binder.getCallingUid();
+ String callingPackage = mPackage.getNameForUid(callingUid);
+ if (Binder.getCallingUid() != uid) {
+ enforceStatsPermission(Binder.getCallingUid(), callingPackage);
+ }
+
+ final String[] packageNames = defeatNullable(mPackage.getPackagesForUid(uid));
+ final PackageStats stats = new PackageStats(TAG);
+ for (int i = 0; i < packageNames.length; i++) {
+ try {
+ appInfo = mPackage.getApplicationInfoAsUser(packageNames[i],
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) {
+ // We don't count code baked into system image
+ } else {
+ computeAppArtStats(stats, packageNames[i]);
+ }
+ } catch (NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+ }
+ return translate(stats);
+ }
+
+ @Override
+ public StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId,
String callingPackage) {
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
@@ -378,9 +424,10 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
callerHasStatsPermission = true;
}
+ StorageStats storageStats;
if (defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length == 1) {
// Only one package inside UID means we can fast-path
- return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
+ storageStats = queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
} else {
// Multiple packages means we need to go manual
final int appId = UserHandle.getAppId(appInfo.uid);
@@ -411,12 +458,16 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
packageName, userHandle, callerHasStatsPermission);
}, "queryStatsForPackage");
}
- return translate(stats);
+ storageStats = translate(stats);
}
+ storageStats.packageName = packageName;
+ storageStats.userHandle = userId;
+ return storageStats;
}
@Override
- public StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage) {
+ public StorageStats queryStatsForUid(String volumeUuid, int uid,
+ String callingPackage) {
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
@@ -481,7 +532,10 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
storageStatsAugmenter.augmentStatsForUid(stats, uid, callerHasStatsPermission);
}, "queryStatsForUid");
}
- return translate(stats);
+ StorageStats storageStats = translate(stats);
+ storageStats.userHandle = userId;
+ storageStats.uid = uid;
+ return storageStats;
}
@Override
@@ -592,8 +646,9 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
}
}
- private static StorageStats translate(PackageStats stats) {
+ private StorageStats translate(PackageStats stats) {
final StorageStats res = new StorageStats();
+ res.userHandle = stats.userHandle;
res.codeBytes = stats.codeSize + stats.externalCodeSize;
res.dataBytes = stats.dataSize + stats.externalDataSize;
res.cacheBytes = stats.cacheSize + stats.externalCacheSize;
@@ -967,6 +1022,12 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
stats.dmSize += getFileBytesInDir(srcDir, ".dm");
stats.libSize += getDirBytes(new File(sourceDirName + "/lib/"));
+ if (!Flags.getAppArtManagedBytes()) {
+ computeAppArtStats(stats, packageName);
+ }
+ }
+
+ private void computeAppArtStats(PackageStats stats, String packageName) {
// Get dexopt, current profle and reference profile sizes.
ArtManagedFileStats artManagedFileStats;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
index a1e165551b5b..c58287760d0e 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
@@ -44,14 +44,12 @@ import org.mockito.junit.MockitoJUnitRunner
/**
* Tests for [InputManager.InputDeviceBatteryListener].
*
- * Build/Install/Run:
- * atest InputTests:InputDeviceBatteryListenerTest
+ * Build/Install/Run: atest InputTests:InputDeviceBatteryListenerTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
class InputDeviceBatteryListenerTest {
- @get:Rule
- val rule = MockitoJUnit.rule()!!
+ @get:Rule val rule = MockitoJUnit.rule()!!
private lateinit var testLooper: TestLooper
private var registeredListener: IInputDeviceBatteryListener? = null
@@ -60,8 +58,7 @@ class InputDeviceBatteryListenerTest {
private lateinit var context: Context
private lateinit var inputManager: InputManager
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
@Before
fun setUp() {
@@ -71,41 +68,48 @@ class InputDeviceBatteryListenerTest {
registeredListener = null
monitoredDevices.clear()
inputManager = InputManager(context)
- `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
// Handle battery listener registration.
doAnswer {
- val deviceId = it.getArgument(0) as Int
- val listener = it.getArgument(1) as IInputDeviceBatteryListener
- if (registeredListener != null &&
- registeredListener!!.asBinder() != listener.asBinder()) {
- // There can only be one registered battery listener per process.
- fail("Trying to register a new listener when one already exists")
+ val deviceId = it.getArgument(0) as Int
+ val listener = it.getArgument(1) as IInputDeviceBatteryListener
+ if (
+ registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ // There can only be one registered battery listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ if (monitoredDevices.contains(deviceId)) {
+ fail("Trying to start monitoring a device that was already being monitored")
+ }
+ monitoredDevices.add(deviceId)
+ registeredListener = listener
+ null
}
- if (monitoredDevices.contains(deviceId)) {
- fail("Trying to start monitoring a device that was already being monitored")
- }
- monitoredDevices.add(deviceId)
- registeredListener = listener
- null
- }.`when`(inputManagerRule.mock).registerBatteryListener(anyInt(), any())
+ .`when`(inputManagerRule.mock)
+ .registerBatteryListener(anyInt(), any())
// Handle battery listener being unregistered.
doAnswer {
- val deviceId = it.getArgument(0) as Int
- val listener = it.getArgument(1) as IInputDeviceBatteryListener
- if (registeredListener == null ||
- registeredListener!!.asBinder() != listener.asBinder()) {
- fail("Trying to unregister a listener that is not registered")
- }
- if (!monitoredDevices.remove(deviceId)) {
- fail("Trying to stop monitoring a device that is not being monitored")
+ val deviceId = it.getArgument(0) as Int
+ val listener = it.getArgument(1) as IInputDeviceBatteryListener
+ if (
+ registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ if (!monitoredDevices.remove(deviceId)) {
+ fail("Trying to stop monitoring a device that is not being monitored")
+ }
+ if (monitoredDevices.isEmpty()) {
+ registeredListener = null
+ }
}
- if (monitoredDevices.isEmpty()) {
- registeredListener = null
- }
- }.`when`(inputManagerRule.mock).unregisterBatteryListener(anyInt(), any())
+ .`when`(inputManagerRule.mock)
+ .unregisterBatteryListener(anyInt(), any())
}
private fun notifyBatteryStateChanged(
@@ -113,15 +117,17 @@ class InputDeviceBatteryListenerTest {
isPresent: Boolean = true,
status: Int = BatteryState.STATUS_FULL,
capacity: Float = 1.0f,
- eventTime: Long = 12345L
+ eventTime: Long = 12345L,
) {
- registeredListener!!.onBatteryStateChanged(IInputDeviceBatteryState().apply {
- this.deviceId = deviceId
- this.updateTime = eventTime
- this.isPresent = isPresent
- this.status = status
- this.capacity = capacity
- })
+ registeredListener!!.onBatteryStateChanged(
+ IInputDeviceBatteryState().apply {
+ this.deviceId = deviceId
+ this.updateTime = eventTime
+ this.isPresent = isPresent
+ this.status = status
+ this.capacity = capacity
+ }
+ )
}
@Test
@@ -130,7 +136,9 @@ class InputDeviceBatteryListenerTest {
// Add a battery listener to monitor battery changes.
inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor) {
- deviceId: Int, eventTime: Long, batteryState: BatteryState ->
+ deviceId: Int,
+ eventTime: Long,
+ batteryState: BatteryState ->
callbackCount++
assertEquals(1, deviceId)
assertEquals(true, batteryState.isPresent)
@@ -149,8 +157,13 @@ class InputDeviceBatteryListenerTest {
assertEquals(0, callbackCount)
// Notifying battery change for the registered device will notify the listener.
- notifyBatteryStateChanged(1 /*deviceId*/, true /*isPresent*/,
- BatteryState.STATUS_DISCHARGING, 0.5f /*capacity*/, 8675309L /*eventTime*/)
+ notifyBatteryStateChanged(
+ 1 /*deviceId*/,
+ true /*isPresent*/,
+ BatteryState.STATUS_DISCHARGING,
+ 0.5f /*capacity*/,
+ 8675309L, /*eventTime*/
+ )
testLooper.dispatchNext()
assertEquals(1, callbackCount)
}
diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt
index 4c6bb849155c..e6bec077d9d4 100644
--- a/tests/Input/src/android/hardware/input/InputManagerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt
@@ -38,8 +38,7 @@ import org.mockito.junit.MockitoJUnitRunner
/**
* Tests for [InputManager].
*
- * Build/Install/Run:
- * atest InputTests:InputManagerTest
+ * Build/Install/Run: atest InputTests:InputManagerTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
@@ -51,8 +50,7 @@ class InputManagerTest {
const val THIRD_DEVICE_ID = 99
}
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
private lateinit var devicesChangedListener: IInputDevicesChangedListener
private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
@@ -64,9 +62,7 @@ class InputManagerTest {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
inputManager = InputManager(context)
`when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
- `when`(inputManagerRule.mock.inputDeviceIds).then {
- deviceGenerationMap.keys.toIntArray()
- }
+ `when`(inputManagerRule.mock.inputDeviceIds).then { deviceGenerationMap.keys.toIntArray() }
}
private fun notifyDeviceChanged(
@@ -74,8 +70,9 @@ class InputManagerTest {
associatedDisplayId: Int,
usiVersion: HostUsiVersion?,
) {
- val generation = deviceGenerationMap[deviceId]?.plus(1)
- ?: throw IllegalArgumentException("Device $deviceId was never added!")
+ val generation =
+ deviceGenerationMap[deviceId]?.plus(1)
+ ?: throw IllegalArgumentException("Device $deviceId was never added!")
deviceGenerationMap[deviceId] = generation
`when`(inputManagerRule.mock.getInputDevice(deviceId))
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
index c62bd0b72584..7f7d4590c322 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -23,6 +23,11 @@ import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
import com.android.test.input.MockInputManagerRule
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
+import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -31,17 +36,11 @@ import org.mockito.Mockito
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnitRunner
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.fail
-import org.junit.Assert.assertThrows
/**
* Tests for [InputManager.KeyGestureEventHandler].
*
- * Build/Install/Run:
- * atest InputTests:KeyGestureEventHandlerTest
+ * Build/Install/Run: atest InputTests:KeyGestureEventHandlerTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
@@ -49,24 +48,24 @@ class KeyGestureEventHandlerTest {
companion object {
const val DEVICE_ID = 1
- val HOME_GESTURE_EVENT = KeyGestureEvent.Builder()
- .setDeviceId(DEVICE_ID)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
- val BACK_GESTURE_EVENT = KeyGestureEvent.Builder()
- .setDeviceId(DEVICE_ID)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_DEL))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build()
+ val HOME_GESTURE_EVENT =
+ KeyGestureEvent.Builder()
+ .setDeviceId(DEVICE_ID)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
+ .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ val BACK_GESTURE_EVENT =
+ KeyGestureEvent.Builder()
+ .setDeviceId(DEVICE_ID)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_DEL))
+ .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build()
}
- @get:Rule
- val rule = SetFlagsRule()
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val rule = SetFlagsRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
private var registeredListener: IKeyGestureHandler? = null
private lateinit var context: Context
@@ -76,31 +75,38 @@ class KeyGestureEventHandlerTest {
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
inputManager = InputManager(context)
- `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
// Handle key gesture handler registration.
doAnswer {
- val listener = it.getArgument(1) as IKeyGestureHandler
- if (registeredListener != null &&
- registeredListener!!.asBinder() != listener.asBinder()) {
- // There can only be one registered key gesture handler per process.
- fail("Trying to register a new listener when one already exists")
+ val listener = it.getArgument(1) as IKeyGestureHandler
+ if (
+ registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ // There can only be one registered key gesture handler per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ registeredListener = listener
+ null
}
- registeredListener = listener
- null
- }.`when`(inputManagerRule.mock).registerKeyGestureHandler(Mockito.any(), Mockito.any())
+ .`when`(inputManagerRule.mock)
+ .registerKeyGestureHandler(Mockito.any(), Mockito.any())
// Handle key gesture handler being unregistered.
doAnswer {
- val listener = it.getArgument(0) as IKeyGestureHandler
- if (registeredListener == null ||
- registeredListener!!.asBinder() != listener.asBinder()) {
- fail("Trying to unregister a listener that is not registered")
+ val listener = it.getArgument(0) as IKeyGestureHandler
+ if (
+ registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ registeredListener = null
+ null
}
- registeredListener = null
- null
- }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(Mockito.any())
+ .`when`(inputManagerRule.mock)
+ .unregisterKeyGestureHandler(Mockito.any())
}
private fun handleKeyGestureEvent(event: KeyGestureEvent) {
@@ -143,7 +149,7 @@ class KeyGestureEventHandlerTest {
// Adding the handler should register the callback with InputManagerService.
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
- callback1
+ callback1,
)
assertNotNull(registeredListener)
@@ -151,7 +157,7 @@ class KeyGestureEventHandlerTest {
val currListener = registeredListener
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
- callback2
+ callback2,
)
assertEquals(currListener, registeredListener)
}
@@ -164,11 +170,11 @@ class KeyGestureEventHandlerTest {
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
- callback1
+ callback1,
)
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
- callback2
+ callback2,
)
// Only removing all handlers should remove the internal callback
@@ -184,24 +190,26 @@ class KeyGestureEventHandlerTest {
var callbackCount1 = 0
var callbackCount2 = 0
// Handler 1 captures all home gestures
- val callback1 = InputManager.KeyGestureEventHandler { event, _ ->
- callbackCount1++
- assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_HOME, event.keyGestureType)
- }
+ val callback1 =
+ InputManager.KeyGestureEventHandler { event, _ ->
+ callbackCount1++
+ assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_HOME, event.keyGestureType)
+ }
// Handler 2 captures all back gestures
- val callback2 = InputManager.KeyGestureEventHandler { event, _ ->
- callbackCount2++
- assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_BACK, event.keyGestureType)
- }
+ val callback2 =
+ InputManager.KeyGestureEventHandler { event, _ ->
+ callbackCount2++
+ assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_BACK, event.keyGestureType)
+ }
// Add both key gesture event handlers
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
- callback1
+ callback1,
)
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
- callback2
+ callback2,
)
// Request handling for home key gesture event, should notify only callback1
@@ -228,12 +236,13 @@ class KeyGestureEventHandlerTest {
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
- handler
+ handler,
)
assertThrows(IllegalArgumentException::class.java) {
inputManager.registerKeyGestureEventHandler(
- listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), handler
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ handler,
)
}
}
@@ -245,12 +254,13 @@ class KeyGestureEventHandlerTest {
inputManager.registerKeyGestureEventHandler(
listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
- handler1
+ handler1,
)
assertThrows(IllegalArgumentException::class.java) {
inputManager.registerKeyGestureEventHandler(
- listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), handler2
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ handler2,
)
}
}
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
index cf0bfcc4f6df..bf3a9c389c7e 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -43,8 +43,7 @@ import org.mockito.junit.MockitoJUnitRunner
/**
* Tests for [InputManager.KeyGestureEventListener].
*
- * Build/Install/Run:
- * atest InputTests:KeyGestureEventListenerTest
+ * Build/Install/Run: atest InputTests:KeyGestureEventListenerTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
@@ -52,18 +51,17 @@ class KeyGestureEventListenerTest {
companion object {
const val DEVICE_ID = 1
- val HOME_GESTURE_EVENT = KeyGestureEvent.Builder()
- .setDeviceId(DEVICE_ID)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
+ val HOME_GESTURE_EVENT =
+ KeyGestureEvent.Builder()
+ .setDeviceId(DEVICE_ID)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
+ .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
}
- @get:Rule
- val rule = SetFlagsRule()
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val rule = SetFlagsRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
private val testLooper = TestLooper()
private val executor = HandlerExecutor(Handler(testLooper.looper))
@@ -75,31 +73,38 @@ class KeyGestureEventListenerTest {
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
inputManager = InputManager(context)
- `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
// Handle key gesture event listener registration.
doAnswer {
- val listener = it.getArgument(0) as IKeyGestureEventListener
- if (registeredListener != null &&
- registeredListener!!.asBinder() != listener.asBinder()) {
- // There can only be one registered key gesture event listener per process.
- fail("Trying to register a new listener when one already exists")
+ val listener = it.getArgument(0) as IKeyGestureEventListener
+ if (
+ registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ // There can only be one registered key gesture event listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ registeredListener = listener
+ null
}
- registeredListener = listener
- null
- }.`when`(inputManagerRule.mock).registerKeyGestureEventListener(any())
+ .`when`(inputManagerRule.mock)
+ .registerKeyGestureEventListener(any())
// Handle key gesture event listener being unregistered.
doAnswer {
- val listener = it.getArgument(0) as IKeyGestureEventListener
- if (registeredListener == null ||
- registeredListener!!.asBinder() != listener.asBinder()) {
- fail("Trying to unregister a listener that is not registered")
+ val listener = it.getArgument(0) as IKeyGestureEventListener
+ if (
+ registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ registeredListener = null
+ null
}
- registeredListener = null
- null
- }.`when`(inputManagerRule.mock).unregisterKeyGestureEventListener(any())
+ .`when`(inputManagerRule.mock)
+ .unregisterKeyGestureEventListener(any())
}
private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
@@ -119,8 +124,7 @@ class KeyGestureEventListenerTest {
var callbackCount = 0
// Add a key gesture event listener
- inputManager.registerKeyGestureEventListener(executor) {
- event: KeyGestureEvent ->
+ inputManager.registerKeyGestureEventListener(executor) { event: KeyGestureEvent ->
assertEquals(HOME_GESTURE_EVENT, event)
callbackCount++
}
diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
index d25dee1d402c..9e419439fba4 100644
--- a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
@@ -42,15 +42,13 @@ import org.mockito.junit.MockitoJUnitRunner
/**
* Tests for [InputManager.KeyboardBacklightListener].
*
- * Build/Install/Run:
- * atest InputTests:KeyboardBacklightListenerTest
+ * Build/Install/Run: atest InputTests:KeyboardBacklightListenerTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
class KeyboardBacklightListenerTest {
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
private lateinit var testLooper: TestLooper
private var registeredListener: IKeyboardBacklightListener? = null
@@ -65,43 +63,54 @@ class KeyboardBacklightListenerTest {
executor = HandlerExecutor(Handler(testLooper.looper))
registeredListener = null
inputManager = InputManager(context)
- `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
// Handle keyboard backlight listener registration.
doAnswer {
- val listener = it.getArgument(0) as IKeyboardBacklightListener
- if (registeredListener != null &&
- registeredListener!!.asBinder() != listener.asBinder()) {
- // There can only be one registered keyboard backlight listener per process.
- fail("Trying to register a new listener when one already exists")
+ val listener = it.getArgument(0) as IKeyboardBacklightListener
+ if (
+ registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ // There can only be one registered keyboard backlight listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ registeredListener = listener
+ null
}
- registeredListener = listener
- null
- }.`when`(inputManagerRule.mock).registerKeyboardBacklightListener(any())
+ .`when`(inputManagerRule.mock)
+ .registerKeyboardBacklightListener(any())
// Handle keyboard backlight listener being unregistered.
doAnswer {
- val listener = it.getArgument(0) as IKeyboardBacklightListener
- if (registeredListener == null ||
- registeredListener!!.asBinder() != listener.asBinder()) {
- fail("Trying to unregister a listener that is not registered")
+ val listener = it.getArgument(0) as IKeyboardBacklightListener
+ if (
+ registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ registeredListener = null
+ null
}
- registeredListener = null
- null
- }.`when`(inputManagerRule.mock).unregisterKeyboardBacklightListener(any())
+ .`when`(inputManagerRule.mock)
+ .unregisterKeyboardBacklightListener(any())
}
private fun notifyKeyboardBacklightChanged(
deviceId: Int,
brightnessLevel: Int,
maxBrightnessLevel: Int = 10,
- isTriggeredByKeyPress: Boolean = true
+ isTriggeredByKeyPress: Boolean = true,
) {
- registeredListener!!.onBrightnessChanged(deviceId, IKeyboardBacklightState().apply {
- this.brightnessLevel = brightnessLevel
- this.maxBrightnessLevel = maxBrightnessLevel
- }, isTriggeredByKeyPress)
+ registeredListener!!.onBrightnessChanged(
+ deviceId,
+ IKeyboardBacklightState().apply {
+ this.brightnessLevel = brightnessLevel
+ this.maxBrightnessLevel = maxBrightnessLevel
+ },
+ isTriggeredByKeyPress,
+ )
}
@Test
@@ -110,9 +119,9 @@ class KeyboardBacklightListenerTest {
// Add a keyboard backlight listener
inputManager.registerKeyboardBacklightListener(executor) {
- deviceId: Int,
- keyboardBacklightState: KeyboardBacklightState,
- isTriggeredByKeyPress: Boolean ->
+ deviceId: Int,
+ keyboardBacklightState: KeyboardBacklightState,
+ isTriggeredByKeyPress: Boolean ->
callbackCount++
assertEquals(1, deviceId)
assertEquals(2, keyboardBacklightState.brightnessLevel)
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
index bcff2fcfca93..a59cbaf5fd55 100644
--- a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -28,8 +28,7 @@ import org.mockito.junit.MockitoJUnitRunner
/**
* Tests for Keyboard layout preview
*
- * Build/Install/Run:
- * atest InputTests:KeyboardLayoutPreviewTests
+ * Build/Install/Run: atest InputTests:KeyboardLayoutPreviewTests
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
@@ -52,4 +51,4 @@ class KeyboardLayoutPreviewTests {
assertEquals(WIDTH, drawable.intrinsicWidth)
assertEquals(HEIGHT, drawable.intrinsicHeight)
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index cc58bbc38e2d..620cb015911e 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -43,15 +43,13 @@ import org.mockito.junit.MockitoJUnitRunner
/**
* Tests for [InputManager.StickyModifierStateListener].
*
- * Build/Install/Run:
- * atest InputTests:StickyModifierStateListenerTest
+ * Build/Install/Run: atest InputTests:StickyModifierStateListenerTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
class StickyModifierStateListenerTest {
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
private val testLooper = TestLooper()
private val executor = HandlerExecutor(Handler(testLooper.looper))
@@ -63,31 +61,38 @@ class StickyModifierStateListenerTest {
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
inputManager = InputManager(context)
- `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
// Handle sticky modifier state listener registration.
doAnswer {
- val listener = it.getArgument(0) as IStickyModifierStateListener
- if (registeredListener != null &&
- registeredListener!!.asBinder() != listener.asBinder()) {
- // There can only be one registered sticky modifier state listener per process.
- fail("Trying to register a new listener when one already exists")
+ val listener = it.getArgument(0) as IStickyModifierStateListener
+ if (
+ registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ // There can only be one registered sticky modifier state listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ registeredListener = listener
+ null
}
- registeredListener = listener
- null
- }.`when`(inputManagerRule.mock).registerStickyModifierStateListener(any())
+ .`when`(inputManagerRule.mock)
+ .registerStickyModifierStateListener(any())
// Handle sticky modifier state listener being unregistered.
doAnswer {
- val listener = it.getArgument(0) as IStickyModifierStateListener
- if (registeredListener == null ||
- registeredListener!!.asBinder() != listener.asBinder()) {
- fail("Trying to unregister a listener that is not registered")
+ val listener = it.getArgument(0) as IStickyModifierStateListener
+ if (
+ registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()
+ ) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ registeredListener = null
+ null
}
- registeredListener = null
- null
- }.`when`(inputManagerRule.mock).unregisterStickyModifierStateListener(any())
+ .`when`(inputManagerRule.mock)
+ .unregisterStickyModifierStateListener(any())
}
private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) {
@@ -99,9 +104,7 @@ class StickyModifierStateListenerTest {
var callbackCount = 0
// Add a sticky modifier state listener
- inputManager.registerStickyModifierStateListener(executor) {
- callbackCount++
- }
+ inputManager.registerStickyModifierStateListener(executor) { callbackCount++ }
// Notifying sticky modifier state change will notify the listener.
notifyStickyModifierStateChanged(0, 0)
@@ -112,8 +115,7 @@ class StickyModifierStateListenerTest {
@Test
fun testListenerHasCorrectModifierStateNotified() {
// Add a sticky modifier state listener
- inputManager.registerStickyModifierStateListener(executor) {
- state: StickyModifierState ->
+ inputManager.registerStickyModifierStateListener(executor) { state: StickyModifierState ->
assertTrue(state.isAltModifierOn)
assertTrue(state.isAltModifierLocked)
assertTrue(state.isShiftModifierOn)
@@ -128,9 +130,11 @@ class StickyModifierStateListenerTest {
// Notifying sticky modifier state change will notify the listener.
notifyStickyModifierStateChanged(
- KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON or
- KeyEvent.META_SHIFT_ON or KeyEvent.META_SHIFT_LEFT_ON,
- KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON
+ KeyEvent.META_ALT_ON or
+ KeyEvent.META_ALT_LEFT_ON or
+ KeyEvent.META_SHIFT_ON or
+ KeyEvent.META_SHIFT_LEFT_ON,
+ KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON,
)
testLooper.dispatchNext()
}
diff --git a/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt
index ad481dff810c..075a3ab8cc73 100644
--- a/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt
@@ -54,8 +54,7 @@ import org.mockito.junit.MockitoJUnit
/**
* Tests for {@link AmbientKeyboardBacklightController}.
*
- * Build/Install/Run:
- * atest InputTests:AmbientKeyboardBacklightControllerTests
+ * Build/Install/Run: atest InputTests:AmbientKeyboardBacklightControllerTests
*/
@Presubmit
class AmbientKeyboardBacklightControllerTests {
@@ -66,24 +65,19 @@ class AmbientKeyboardBacklightControllerTests {
const val SENSOR_TYPE = "test_sensor_type"
}
- @get:Rule
- val rule = MockitoJUnit.rule()!!
+ @get:Rule val rule = MockitoJUnit.rule()!!
private lateinit var context: Context
private lateinit var testLooper: TestLooper
private lateinit var ambientController: AmbientKeyboardBacklightController
- @Mock
- private lateinit var resources: Resources
+ @Mock private lateinit var resources: Resources
- @Mock
- private lateinit var lightSensorInfo: InputSensorInfo
+ @Mock private lateinit var lightSensorInfo: InputSensorInfo
- @Mock
- private lateinit var sensorManager: SensorManager
+ @Mock private lateinit var sensorManager: SensorManager
- @Mock
- private lateinit var displayManagerInternal: DisplayManagerInternal
+ @Mock private lateinit var displayManagerInternal: DisplayManagerInternal
private lateinit var lightSensor: Sensor
private var currentDisplayInfo = DisplayInfo()
@@ -114,26 +108,26 @@ class AmbientKeyboardBacklightControllerTests {
`when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold))
.thenReturn(increaseThresholds)
`when`(
- resources.getValue(
- eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
- any(TypedValue::class.java),
- anyBoolean()
+ resources.getValue(
+ eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
+ any(TypedValue::class.java),
+ anyBoolean(),
+ )
)
- ).then {
- val args = it.arguments
- val outValue = args[1] as TypedValue
- outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
- Unit
- }
+ .then {
+ val args = it.arguments
+ val outValue = args[1] as TypedValue
+ outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
+ Unit
+ }
}
private fun setupSensor() {
LocalServices.removeServiceForTest(DisplayManagerInternal::class.java)
LocalServices.addService(DisplayManagerInternal::class.java, displayManagerInternal)
currentDisplayInfo.uniqueId = DEFAULT_DISPLAY_UNIQUE_ID
- `when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(
- currentDisplayInfo
- )
+ `when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY))
+ .thenReturn(currentDisplayInfo)
val sensorData = DisplayManagerInternal.AmbientLightSensorData(SENSOR_NAME, SENSOR_TYPE)
`when`(displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY))
.thenReturn(sensorData)
@@ -144,26 +138,28 @@ class AmbientKeyboardBacklightControllerTests {
`when`(context.getSystemService(eq(Context.SENSOR_SERVICE))).thenReturn(sensorManager)
`when`(sensorManager.getSensorList(anyInt())).thenReturn(listOf(lightSensor))
`when`(
- sensorManager.registerListener(
- any(),
- eq(lightSensor),
- anyInt(),
- any(Handler::class.java)
+ sensorManager.registerListener(
+ any(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler::class.java),
+ )
)
- ).then {
- listenerRegistered = true
- listenerRegistrationCount++
- true
- }
+ .then {
+ listenerRegistered = true
+ listenerRegistrationCount++
+ true
+ }
`when`(
- sensorManager.unregisterListener(
- any(SensorEventListener::class.java),
- eq(lightSensor)
+ sensorManager.unregisterListener(
+ any(SensorEventListener::class.java),
+ eq(lightSensor),
+ )
)
- ).then {
- listenerRegistered = false
- Unit
- }
+ .then {
+ listenerRegistered = false
+ Unit
+ }
}
private fun setupSensorWithInitialLux(luxValue: Float) {
@@ -181,7 +177,7 @@ class AmbientKeyboardBacklightControllerTests {
assertEquals(
"Should receive immediate callback for first lux change",
100,
- lastBrightnessCallback
+ lastBrightnessCallback,
)
}
@@ -190,15 +186,13 @@ class AmbientKeyboardBacklightControllerTests {
setupSensorWithInitialLux(500F)
// Current state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
- repeat(HYSTERESIS_THRESHOLD) {
- sendAmbientLuxValue(1500F)
- }
+ repeat(HYSTERESIS_THRESHOLD) { sendAmbientLuxValue(1500F) }
testLooper.dispatchAll()
assertEquals(
"Should receive brightness change callback for increasing lux change",
200,
- lastBrightnessCallback
+ lastBrightnessCallback,
)
}
@@ -207,39 +201,31 @@ class AmbientKeyboardBacklightControllerTests {
setupSensorWithInitialLux(1500F)
// Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
- repeat(HYSTERESIS_THRESHOLD) {
- sendAmbientLuxValue(500F)
- }
+ repeat(HYSTERESIS_THRESHOLD) { sendAmbientLuxValue(500F) }
testLooper.dispatchAll()
assertEquals(
"Should receive brightness change callback for decreasing lux change",
100,
- lastBrightnessCallback
+ lastBrightnessCallback,
)
}
@Test
fun testRegisterAmbientListener_throwsExceptionOnRegisteringDuplicate() {
- val ambientListener =
- AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ val ambientListener = AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {}
ambientController.registerAmbientBacklightListener(ambientListener)
assertThrows(IllegalStateException::class.java) {
- ambientController.registerAmbientBacklightListener(
- ambientListener
- )
+ ambientController.registerAmbientBacklightListener(ambientListener)
}
}
@Test
fun testUnregisterAmbientListener_throwsExceptionOnUnregisteringNonExistent() {
- val ambientListener =
- AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ val ambientListener = AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {}
assertThrows(IllegalStateException::class.java) {
- ambientController.unregisterAmbientBacklightListener(
- ambientListener
- )
+ ambientController.unregisterAmbientBacklightListener(ambientListener)
}
}
@@ -248,25 +234,23 @@ class AmbientKeyboardBacklightControllerTests {
assertEquals(
"Should not have a sensor listener registered at init",
0,
- listenerRegistrationCount
+ listenerRegistrationCount,
)
assertFalse("Should not have a sensor listener registered at init", listenerRegistered)
val ambientListener1 =
- AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {}
ambientController.registerAmbientBacklightListener(ambientListener1)
- assertEquals(
- "Should register a new sensor listener", 1, listenerRegistrationCount
- )
+ assertEquals("Should register a new sensor listener", 1, listenerRegistrationCount)
assertTrue("Should have sensor listener registered", listenerRegistered)
val ambientListener2 =
- AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {}
ambientController.registerAmbientBacklightListener(ambientListener2)
assertEquals(
"Should not register a new sensor listener when adding a second ambient listener",
1,
- listenerRegistrationCount
+ listenerRegistrationCount,
)
assertTrue("Should have sensor listener registered", listenerRegistered)
@@ -276,7 +260,7 @@ class AmbientKeyboardBacklightControllerTests {
ambientController.unregisterAmbientBacklightListener(ambientListener2)
assertFalse(
"Should not have sensor listener registered if there are no ambient listeners",
- listenerRegistered
+ listenerRegistered,
)
}
@@ -291,7 +275,7 @@ class AmbientKeyboardBacklightControllerTests {
assertEquals(
"Should not re-register listener on display change if unique is same",
count,
- listenerRegistrationCount
+ listenerRegistrationCount,
)
}
@@ -307,7 +291,7 @@ class AmbientKeyboardBacklightControllerTests {
assertEquals(
"Should re-register listener on display change if unique id changed",
count + 1,
- listenerRegistrationCount
+ listenerRegistrationCount,
)
}
@@ -318,15 +302,13 @@ class AmbientKeyboardBacklightControllerTests {
// Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
// Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
lastBrightnessCallback = -1
- repeat(HYSTERESIS_THRESHOLD) {
- sendAmbientLuxValue(999F)
- }
+ repeat(HYSTERESIS_THRESHOLD) { sendAmbientLuxValue(999F) }
testLooper.dispatchAll()
assertEquals(
"Should not receive any callback for brightness change",
-1,
- lastBrightnessCallback
+ lastBrightnessCallback,
)
}
@@ -337,15 +319,13 @@ class AmbientKeyboardBacklightControllerTests {
// Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
// Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
lastBrightnessCallback = -1
- repeat(HYSTERESIS_THRESHOLD - 1) {
- sendAmbientLuxValue(2001F)
- }
+ repeat(HYSTERESIS_THRESHOLD - 1) { sendAmbientLuxValue(2001F) }
testLooper.dispatchAll()
assertEquals(
"Should not receive any callback for brightness change",
-1,
- lastBrightnessCallback
+ lastBrightnessCallback,
)
}
diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
index 890c346ea015..bcb740652dde 100644
--- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
@@ -92,7 +92,7 @@ private fun createInputDevice(
private fun <T, U> memberMatcher(
member: String,
memberProvider: (T) -> U,
- match: Matcher<U>
+ match: Matcher<U>,
): TypeSafeMatcher<T> =
object : TypeSafeMatcher<T>() {
@@ -115,12 +115,13 @@ private fun matchesState(
isPresent: Boolean = true,
status: Int? = null,
capacity: Float? = null,
- eventTime: Long? = null
+ eventTime: Long? = null,
): Matcher<IInputDeviceBatteryState> {
- val batteryStateMatchers = mutableListOf<Matcher<IInputDeviceBatteryState>>(
- memberMatcher("deviceId", { it.deviceId }, equalTo(deviceId)),
- memberMatcher("isPresent", { it.isPresent }, equalTo(isPresent))
- )
+ val batteryStateMatchers =
+ mutableListOf<Matcher<IInputDeviceBatteryState>>(
+ memberMatcher("deviceId", { it.deviceId }, equalTo(deviceId)),
+ memberMatcher("isPresent", { it.isPresent }, equalTo(isPresent)),
+ )
if (eventTime != null) {
batteryStateMatchers.add(memberMatcher("updateTime", { it.updateTime }, equalTo(eventTime)))
}
@@ -143,14 +144,14 @@ private fun IInputDeviceBatteryListener.verifyNotified(
isPresent: Boolean = true,
status: Int? = null,
capacity: Float? = null,
- eventTime: Long? = null
+ eventTime: Long? = null,
) {
verifyNotified(matchesState(deviceId, isPresent, status, capacity, eventTime), mode)
}
private fun IInputDeviceBatteryListener.verifyNotified(
matcher: Matcher<IInputDeviceBatteryState>,
- mode: VerificationMode = times(1)
+ mode: VerificationMode = times(1),
) {
verify(this, mode).onBatteryStateChanged(MockitoHamcrest.argThat(matcher))
}
@@ -165,8 +166,7 @@ private fun createMockListener(): IInputDeviceBatteryListener {
/**
* Tests for {@link InputDeviceBatteryController}.
*
- * Build/Install/Run:
- * atest InputTests:InputDeviceBatteryControllerTests
+ * Build/Install/Run: atest InputTests:InputDeviceBatteryControllerTests
*/
@Presubmit
class BatteryControllerTests {
@@ -181,19 +181,13 @@ class BatteryControllerTests {
const val TIMESTAMP = 123456789L
}
- @get:Rule
- val rule = MockitoJUnit.rule()!!
- @get:Rule
- val context = TestableContext(ApplicationProvider.getApplicationContext())
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val rule = MockitoJUnit.rule()!!
+ @get:Rule val context = TestableContext(ApplicationProvider.getApplicationContext())
+ @get:Rule val inputManagerRule = MockInputManagerRule()
- @Mock
- private lateinit var native: NativeInputManagerService
- @Mock
- private lateinit var uEventManager: UEventManager
- @Mock
- private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
+ @Mock private lateinit var native: NativeInputManagerService
+ @Mock private lateinit var uEventManager: UEventManager
+ @Mock private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
private lateinit var batteryController: BatteryController
private lateinit var testLooper: TestLooper
@@ -206,14 +200,18 @@ class BatteryControllerTests {
testLooper = TestLooper()
val inputManager = InputManager(context)
context.addMockSystemService(InputManager::class.java, inputManager)
- `when`(inputManagerRule.mock.inputDeviceIds).then {
- deviceGenerationMap.keys.toIntArray()
- }
+ `when`(inputManagerRule.mock.inputDeviceIds).then { deviceGenerationMap.keys.toIntArray() }
addInputDevice(DEVICE_ID)
addInputDevice(SECOND_DEVICE_ID)
- batteryController = BatteryController(context, native, testLooper.looper, uEventManager,
- bluetoothBatteryManager)
+ batteryController =
+ BatteryController(
+ context,
+ native,
+ testLooper.looper,
+ uEventManager,
+ bluetoothBatteryManager,
+ )
batteryController.systemRunning()
val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
verify(inputManagerRule.mock).registerInputDevicesChangedListener(listenerCaptor.capture())
@@ -222,12 +220,13 @@ class BatteryControllerTests {
}
private fun notifyDeviceChanged(
- deviceId: Int,
+ deviceId: Int,
hasBattery: Boolean = true,
- supportsUsi: Boolean = false
+ supportsUsi: Boolean = false,
) {
- val generation = deviceGenerationMap[deviceId]?.plus(1)
- ?: throw IllegalArgumentException("Device $deviceId was never added!")
+ val generation =
+ deviceGenerationMap[deviceId]?.plus(1)
+ ?: throw IllegalArgumentException("Device $deviceId was never added!")
deviceGenerationMap[deviceId] = generation
`when`(inputManagerRule.mock.getInputDevice(deviceId))
@@ -239,7 +238,7 @@ class BatteryControllerTests {
}
private fun addInputDevice(
- deviceId: Int,
+ deviceId: Int,
hasBattery: Boolean = true,
supportsUsi: Boolean = false,
) {
@@ -248,8 +247,10 @@ class BatteryControllerTests {
}
private fun createBluetoothDevice(address: String): BluetoothDevice {
- return context.getSystemService(BluetoothManager::class.java)!!
- .adapter.getRemoteDevice(address)
+ return context
+ .getSystemService(BluetoothManager::class.java)!!
+ .adapter
+ .getRemoteDevice(address)
}
@Test
@@ -279,8 +280,7 @@ class BatteryControllerTests {
try {
batteryController.registerBatteryListener(DEVICE_ID, listener2, PID)
fail("Expected security exception when registering more than one listener per process")
- } catch (ignored: SecurityException) {
- }
+ } catch (ignored: SecurityException) {}
}
@Test
@@ -323,15 +323,18 @@ class BatteryControllerTests {
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
// The device paths for UEvent notifications do not include the "/sys" prefix, so verify
// that the added listener is configured to match the path without that prefix.
- verify(uEventManager)
- .addListener(uEventListener.capture(), eq("DEVPATH=/dev/test/device1"))
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/dev/test/device1"))
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
// If the battery state has changed when an UEvent is sent, the listeners are notified.
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
- listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f,
- eventTime = TIMESTAMP)
+ listener.verifyNotified(
+ DEVICE_ID,
+ status = STATUS_CHARGING,
+ capacity = 0.80f,
+ eventTime = TIMESTAMP,
+ )
// If the battery state has not changed when an UEvent is sent, the listeners are not
// notified.
@@ -341,8 +344,11 @@ class BatteryControllerTests {
batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
verify(uEventManager).removeListener(uEventListener.capture())
- assertEquals("The same observer must be registered and unregistered",
- uEventListener.allValues[0], uEventListener.allValues[1])
+ assertEquals(
+ "The same observer must be registered and unregistered",
+ uEventListener.allValues[0],
+ uEventListener.allValues[1],
+ )
}
@Test
@@ -366,8 +372,12 @@ class BatteryControllerTests {
// If the battery becomes present again, the listener is notified.
notifyDeviceChanged(DEVICE_ID, hasBattery = true)
testLooper.dispatchNext()
- listener.verifyNotified(DEVICE_ID, mode = times(2), status = STATUS_CHARGING,
- capacity = 0.78f)
+ listener.verifyNotified(
+ DEVICE_ID,
+ mode = times(2),
+ status = STATUS_CHARGING,
+ capacity = 0.78f,
+ )
// Ensure that a new UEventListener was added.
verify(uEventManager, times(2))
.addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
@@ -437,8 +447,11 @@ class BatteryControllerTests {
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
val batteryState = batteryController.getBatteryState(DEVICE_ID)
- assertThat("battery state matches", batteryState,
- matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f))
+ assertThat(
+ "battery state matches",
+ batteryState,
+ matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f),
+ )
}
@Test
@@ -453,8 +466,11 @@ class BatteryControllerTests {
// change in the battery state, the listener is also notified.
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
val batteryState = batteryController.getBatteryState(DEVICE_ID)
- assertThat("battery matches state", batteryState,
- matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f))
+ assertThat(
+ "battery matches state",
+ batteryState,
+ matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f),
+ )
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f)
}
@@ -466,8 +482,7 @@ class BatteryControllerTests {
// Even though there is no listener added for this device, it is being monitored.
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
- verify(uEventManager)
- .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
// Add and remove a listener for the device.
val listener = createMockListener()
@@ -507,8 +522,7 @@ class BatteryControllerTests {
addInputDevice(USI_DEVICE_ID, supportsUsi = true)
testLooper.dispatchNext()
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
- verify(uEventManager)
- .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
// A USI device's battery state is not valid until the first UEvent notification.
// Add a listener, and ensure it is notified that the battery state is not present.
@@ -517,34 +531,49 @@ class BatteryControllerTests {
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
// Ensure that querying for battery state also returns the same invalid result.
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID),
+ )
// There is a UEvent signaling a battery change. The battery state is now valid.
uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f),
+ )
// There is another UEvent notification. The battery state is now updated.
`when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(64)
uEventListener.value!!.onBatteryUEvent(TIMESTAMP + 1)
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f)
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f),
+ )
// The battery state is still valid after a millisecond.
testLooper.moveTimeForward(1)
testLooper.dispatchAll()
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f),
+ )
// The battery is no longer present after the timeout expires.
testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 1)
testLooper.dispatchNext()
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID), times(2))
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID),
+ )
}
@Test
@@ -556,16 +585,18 @@ class BatteryControllerTests {
addInputDevice(USI_DEVICE_ID, supportsUsi = true)
testLooper.dispatchNext()
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
- verify(uEventManager)
- .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
// There is a UEvent signaling a battery change. The battery state is now valid.
uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
val listener = createMockListener()
batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f),
+ )
// Stylus presence is detected before the validity timeout expires.
testLooper.moveTimeForward(100)
@@ -575,15 +606,21 @@ class BatteryControllerTests {
// Ensure that timeout was extended, and the battery state is now valid for longer.
testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 100)
testLooper.dispatchAll()
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f),
+ )
// Ensure the validity period expires after the expected amount of time.
testLooper.moveTimeForward(100)
testLooper.dispatchNext()
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID),
+ )
}
@Test
@@ -595,22 +632,27 @@ class BatteryControllerTests {
addInputDevice(USI_DEVICE_ID, supportsUsi = true)
testLooper.dispatchNext()
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
- verify(uEventManager)
- .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
// The USI battery state is initially invalid.
val listener = createMockListener()
batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID),
+ )
// A stylus presence is detected. This validates the battery state.
batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f),
+ )
}
@Test
@@ -623,27 +665,35 @@ class BatteryControllerTests {
addInputDevice(USI_DEVICE_ID, supportsUsi = true)
testLooper.dispatchNext()
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
- verify(uEventManager)
- .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
// The USI battery state is initially invalid.
val listener = createMockListener()
batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID),
+ )
// Since the capacity reported is 0, stylus presence does not validate the battery state.
batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID),
+ )
// However, if a UEvent reports a battery capacity of 0, the battery state is now valid.
uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f)
- assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
- matchesState(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f),
+ )
}
@Test
@@ -722,15 +772,21 @@ class BatteryControllerTests {
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
verify(uEventManager).addListener(uEventListener.capture(), any())
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
- assertThat("battery state matches", batteryController.getBatteryState(BT_DEVICE_ID),
- matchesState(BT_DEVICE_ID, capacity = 0.21f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(BT_DEVICE_ID),
+ matchesState(BT_DEVICE_ID, capacity = 0.21f),
+ )
// If only the native battery state changes the listener is not notified.
`when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(97)
uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
- assertThat("battery state matches", batteryController.getBatteryState(BT_DEVICE_ID),
- matchesState(BT_DEVICE_ID, capacity = 0.21f))
+ assertThat(
+ "battery state matches",
+ batteryController.getBatteryState(BT_DEVICE_ID),
+ matchesState(BT_DEVICE_ID, capacity = 0.21f),
+ )
}
@Test
@@ -751,8 +807,11 @@ class BatteryControllerTests {
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
// Fall back to the native state when BT is off.
- bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF",
- BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF)
+ bluetoothListener.value!!.onBluetoothBatteryChanged(
+ TIMESTAMP,
+ "AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF,
+ )
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.98f)
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 22)
@@ -760,8 +819,11 @@ class BatteryControllerTests {
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.22f)
// Fall back to the native state when BT battery is unknown.
- bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF",
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
+ bluetoothListener.value!!.onBluetoothBatteryChanged(
+ TIMESTAMP,
+ "AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ )
listener.verifyNotified(BT_DEVICE_ID, mode = times(2), capacity = 0.98f)
}
@@ -782,10 +844,10 @@ class BatteryControllerTests {
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
verify(bluetoothBatteryManager, never()).addMetadataListener(any(), any())
- val metadataListener1 = ArgumentCaptor.forClass(
- BluetoothAdapter.OnMetadataChangedListener::class.java)
- val metadataListener2 = ArgumentCaptor.forClass(
- BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val metadataListener1 =
+ ArgumentCaptor.forClass(BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val metadataListener2 =
+ ArgumentCaptor.forClass(BluetoothAdapter.OnMetadataChangedListener::class.java)
// The metadata listener is added when the first BT input device is monitored.
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
@@ -814,12 +876,16 @@ class BatteryControllerTests {
fun testNotifiesBluetoothMetadataBatteryChanges() {
`when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
- `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
- BluetoothDevice.METADATA_MAIN_BATTERY))
+ `when`(
+ bluetoothBatteryManager.getMetadata(
+ "AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.METADATA_MAIN_BATTERY,
+ )
+ )
.thenReturn("21".toByteArray())
addInputDevice(BT_DEVICE_ID)
- val metadataListener = ArgumentCaptor.forClass(
- BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val metadataListener =
+ ArgumentCaptor.forClass(BluetoothAdapter.OnMetadataChangedListener::class.java)
val listener = createMockListener()
val bluetoothDevice = createBluetoothDevice("AA:BB:CC:DD:EE:FF")
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
@@ -829,25 +895,44 @@ class BatteryControllerTests {
// When the state has not changed, the listener is not notified again.
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "21".toByteArray())
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_BATTERY,
+ "21".toByteArray(),
+ )
listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "25".toByteArray())
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_BATTERY,
+ "25".toByteArray(),
+ )
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_UNKNOWN)
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray())
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_CHARGING,
+ "true".toByteArray(),
+ )
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_CHARGING)
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "false".toByteArray())
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_CHARGING,
+ "false".toByteArray(),
+ )
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_DISCHARGING)
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, null)
- listener.verifyNotified(BT_DEVICE_ID, mode = times(2), capacity = 0.25f,
- status = STATUS_UNKNOWN)
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_CHARGING,
+ null,
+ )
+ listener.verifyNotified(
+ BT_DEVICE_ID,
+ mode = times(2),
+ capacity = 0.25f,
+ status = STATUS_UNKNOWN,
+ )
}
@Test
@@ -855,13 +940,17 @@ class BatteryControllerTests {
`when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
- `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
- BluetoothDevice.METADATA_MAIN_BATTERY))
+ `when`(
+ bluetoothBatteryManager.getMetadata(
+ "AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.METADATA_MAIN_BATTERY,
+ )
+ )
.thenReturn("22".toByteArray())
addInputDevice(BT_DEVICE_ID)
val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
- val metadataListener = ArgumentCaptor.forClass(
- BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val metadataListener =
+ ArgumentCaptor.forClass(BluetoothAdapter.OnMetadataChangedListener::class.java)
val listener = createMockListener()
val bluetoothDevice = createBluetoothDevice("AA:BB:CC:DD:EE:FF")
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
@@ -877,13 +966,19 @@ class BatteryControllerTests {
listener.verifyNotified(BT_DEVICE_ID, mode = never(), capacity = 0.23f)
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "24".toByteArray())
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_BATTERY,
+ "24".toByteArray(),
+ )
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.24f)
// When the battery level from the metadata is no longer valid, we fall back to using the
// Bluetooth battery level.
metadataListener.value!!.onMetadataChanged(
- bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, null)
+ bluetoothDevice,
+ BluetoothDevice.METADATA_MAIN_BATTERY,
+ null,
+ )
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.23f)
}
}
diff --git a/tests/Input/src/com/android/server/input/InputDataStoreTests.kt b/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
index 78c828bafd8f..812bd608ed20 100644
--- a/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
+++ b/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
@@ -41,8 +41,7 @@ import org.mockito.Mockito
/**
* Tests for {@link InputDataStore}.
*
- * Build/Install/Run:
- * atest InputTests:InputDataStoreTests
+ * Build/Install/Run: atest InputTests:InputDataStoreTests
*/
@Presubmit
class InputDataStoreTests {
@@ -63,25 +62,32 @@ class InputDataStoreTests {
private fun setupInputDataStore() {
tempFile = File.createTempFile("input_gestures", ".xml")
- inputDataStore = InputDataStore(object : InputDataStore.FileInjector("input_gestures") {
- private val atomicFile: AtomicFile = AtomicFile(tempFile)
+ inputDataStore =
+ InputDataStore(
+ object : InputDataStore.FileInjector("input_gestures") {
+ private val atomicFile: AtomicFile = AtomicFile(tempFile)
- override fun openRead(userId: Int): InputStream? {
- return atomicFile.openRead()
- }
+ override fun openRead(userId: Int): InputStream? {
+ return atomicFile.openRead()
+ }
- override fun startWrite(userId: Int): FileOutputStream? {
- return atomicFile.startWrite()
- }
+ override fun startWrite(userId: Int): FileOutputStream? {
+ return atomicFile.startWrite()
+ }
- override fun finishWrite(userId: Int, fos: FileOutputStream?, success: Boolean) {
- if (success) {
- atomicFile.finishWrite(fos)
- } else {
- atomicFile.failWrite(fos)
+ override fun finishWrite(
+ userId: Int,
+ fos: FileOutputStream?,
+ success: Boolean,
+ ) {
+ if (success) {
+ atomicFile.finishWrite(fos)
+ } else {
+ atomicFile.failWrite(fos)
+ }
+ }
}
- }
- })
+ )
}
private fun getPrintableXml(inputGestures: List<InputGestureData>): String {
@@ -92,164 +98,157 @@ class InputDataStoreTests {
@Test
fun saveToDiskKeyGesturesOnly() {
- val inputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val inputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
- .build()
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
inputDataStore.saveInputGestures(USER_ID, inputGestures)
assertEquals(
inputGestures,
inputDataStore.loadInputGestures(USER_ID),
- getPrintableXml(inputGestures)
+ getPrintableXml(inputGestures),
)
}
@Test
fun saveToDiskTouchpadGestures() {
- val inputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ val inputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
inputDataStore.saveInputGestures(USER_ID, inputGestures)
assertEquals(
inputGestures,
inputDataStore.loadInputGestures(USER_ID),
- getPrintableXml(inputGestures)
+ getPrintableXml(inputGestures),
)
}
@Test
fun saveToDiskAppLaunchGestures() {
- val inputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ val inputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
- .setAppLaunchData(AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER))
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
- .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS))
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
- .setAppLaunchData(
- AppLaunchData.createLaunchDataForComponent(
- "com.test",
- "com.test.BookmarkTest"
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
)
- )
- .build()
- )
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_1, KeyEvent.META_META_ON)
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(
+ AppLaunchData.createLaunchDataForComponent(
+ "com.test",
+ "com.test.BookmarkTest",
+ )
+ )
+ .build(),
+ )
inputDataStore.saveInputGestures(USER_ID, inputGestures)
assertEquals(
inputGestures,
inputDataStore.loadInputGestures(USER_ID),
- getPrintableXml(inputGestures)
+ getPrintableXml(inputGestures),
)
}
@Test
fun saveToDiskCombinedGestures() {
- val inputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ val inputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_9,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_9, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
- .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS))
- .build(),
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ )
+ .build(),
+ )
inputDataStore.saveInputGestures(USER_ID, inputGestures)
assertEquals(
inputGestures,
inputDataStore.loadInputGestures(USER_ID),
- getPrintableXml(inputGestures)
+ getPrintableXml(inputGestures),
)
}
@Test
fun validXmlParse() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<input_gesture_list>
@@ -263,45 +262,42 @@ class InputDataStoreTests {
<touchpad_trigger touchpad_gesture_type="1" />
</input_gesture>
</input_gesture_list>
- </root>""".trimIndent()
- val validInputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ </root>"""
+ .trimIndent()
+ val validInputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ )
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- validInputGestures,
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(validInputGestures, inputDataStore.readInputGesturesXml(inputStream, true))
}
@Test
fun missingTriggerData() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<input_gesture_list>
@@ -314,36 +310,33 @@ class InputDataStoreTests {
<touchpad_trigger touchpad_gesture_type="1" />
</input_gesture>
</input_gesture_list>
- </root>""".trimIndent()
- val validInputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ </root>"""
+ .trimIndent()
+ val validInputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ )
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- validInputGestures,
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(validInputGestures, inputDataStore.readInputGesturesXml(inputStream, true))
}
@Test
fun invalidKeycode() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<input_gesture_list>
@@ -357,36 +350,36 @@ class InputDataStoreTests {
<touchpad_trigger touchpad_gesture_type="1" />
</input_gesture>
</input_gesture_list>
- </root>""".trimIndent()
- val validInputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ </root>"""
+ .trimIndent()
+ val validInputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ )
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- validInputGestures,
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(validInputGestures, inputDataStore.readInputGesturesXml(inputStream, true))
}
@Test
fun invalidTriggerName() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<input_gesture_list>
@@ -400,37 +393,34 @@ class InputDataStoreTests {
<invalid_trigger_name touchpad_gesture_type="1" />
</input_gesture>
</input_gesture_list>
- </root>""".trimIndent()
- val validInputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ </root>"""
+ .trimIndent()
+ val validInputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
- .build(),
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- validInputGestures,
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(validInputGestures, inputDataStore.readInputGesturesXml(inputStream, true))
}
@Test
fun invalidTouchpadGestureType() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<input_gesture_list>
@@ -444,61 +434,55 @@ class InputDataStoreTests {
<touchpad_trigger touchpad_gesture_type="9999" />
</input_gesture>
</input_gesture_list>
- </root>""".trimIndent()
- val validInputGestures = listOf(
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_1,
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ </root>"""
+ .trimIndent()
+ val validInputGestures =
+ listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build(),
- InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_2,
- KeyEvent.META_META_ON
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_2, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
- .build(),
- )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- validInputGestures,
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(validInputGestures, inputDataStore.readInputGesturesXml(inputStream, true))
}
@Test
fun emptyInputGestureList() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<input_gesture_list>
</input_gesture_list>
- </root>""".trimIndent()
+ </root>"""
+ .trimIndent()
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- listOf(),
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(listOf(), inputDataStore.readInputGesturesXml(inputStream, true))
}
@Test
fun invalidTag() {
- val xmlData = """
+ val xmlData =
+ """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<invalid_tag_name>
</invalid_tag_name>
- </root>""".trimIndent()
+ </root>"""
+ .trimIndent()
val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
- assertEquals(
- listOf(),
- inputDataStore.readInputGesturesXml(inputStream, true)
- )
+ assertEquals(listOf(), inputDataStore.readInputGesturesXml(inputStream, true))
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
index e281a3fb1287..5b4a39b49146 100644
--- a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
@@ -29,8 +29,7 @@ import org.junit.Test
/**
* Tests for custom keyboard glyph map configuration.
*
- * Build/Install/Run:
- * atest InputTests:CustomInputGestureManagerTests
+ * Build/Install/Run: atest InputTests:CustomInputGestureManagerTests
*/
@Presubmit
class InputGestureManagerTests {
@@ -48,163 +47,144 @@ class InputGestureManagerTests {
@Test
fun addRemoveCustomGesture() {
- val customGesture = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val customGesture =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
val result = inputGestureManager.addCustomInputGesture(USER_ID, customGesture)
assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS, result)
assertEquals(
listOf(customGesture),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
inputGestureManager.removeCustomInputGesture(USER_ID, customGesture)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
}
@Test
fun removeNonExistentGesture() {
- val customGesture = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val customGesture =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
val result = inputGestureManager.removeCustomInputGesture(USER_ID, customGesture)
assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST, result)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
}
@Test
fun addAlreadyExistentGesture() {
- val customGesture = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val customGesture =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
inputGestureManager.addCustomInputGesture(USER_ID, customGesture)
- val customGesture2 = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val customGesture2 =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build()
val result = inputGestureManager.addCustomInputGesture(USER_ID, customGesture2)
assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS, result)
assertEquals(
listOf(customGesture),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
}
@Test
fun addRemoveAllExistentGestures() {
- val customGesture = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val customGesture =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
inputGestureManager.addCustomInputGesture(USER_ID, customGesture)
- val customGesture2 = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_DEL,
- KeyEvent.META_META_ON
+ val customGesture2 =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build()
inputGestureManager.addCustomInputGesture(USER_ID, customGesture2)
assertEquals(
listOf(customGesture, customGesture2),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
- inputGestureManager.removeAllCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.removeAllCustomInputGestures(USER_ID, /* filter= */ null)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
}
@Test
fun filteringBasedOnTouchpadOrKeyGestures() {
- val customKeyGesture = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createKeyTrigger(
- KeyEvent.KEYCODE_H,
- KeyEvent.META_META_ON
+ val customKeyGesture =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON)
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
inputGestureManager.addCustomInputGesture(USER_ID, customKeyGesture)
- val customTouchpadGesture = InputGestureData.Builder()
- .setTrigger(
- InputGestureData.createTouchpadTrigger(
- InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ val customTouchpadGesture =
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
)
- )
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
- .build()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build()
inputGestureManager.addCustomInputGesture(USER_ID, customTouchpadGesture)
assertEquals(
listOf(customTouchpadGesture, customKeyGesture),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
assertEquals(
listOf(customKeyGesture),
- inputGestureManager.getCustomInputGestures(USER_ID, InputGestureData.Filter.KEY)
+ inputGestureManager.getCustomInputGestures(USER_ID, InputGestureData.Filter.KEY),
)
assertEquals(
listOf(customTouchpadGesture),
- inputGestureManager.getCustomInputGestures(
- USER_ID,
- InputGestureData.Filter.TOUCHPAD
- )
+ inputGestureManager.getCustomInputGestures(USER_ID, InputGestureData.Filter.TOUCHPAD),
)
inputGestureManager.removeAllCustomInputGestures(USER_ID, InputGestureData.Filter.KEY)
assertEquals(
listOf(customTouchpadGesture),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
- inputGestureManager.removeAllCustomInputGestures(
- USER_ID,
- InputGestureData.Filter.TOUCHPAD
- )
+ inputGestureManager.removeAllCustomInputGestures(USER_ID, InputGestureData.Filter.TOUCHPAD)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter= */ null),
)
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 71c7a6b1119d..4737d19acde1 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -16,7 +16,6 @@
package com.android.server.input
-
import android.Manifest
import android.content.Context
import android.content.ContextWrapper
@@ -39,14 +38,14 @@ import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
-import android.view.View.OnKeyListener
+import android.test.mock.MockContentResolver
import android.view.InputDevice
import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
+import android.view.View.OnKeyListener
import android.view.WindowManager
-import android.test.mock.MockContentResolver
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.dx.mockito.inline.extended.ExtendedMockito
@@ -75,28 +74,33 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyNoInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when`
import org.mockito.stubbing.OngoingStubbing
/**
* Tests for {@link InputManagerService}.
*
- * Build/Install/Run:
- * atest InputTests:InputManagerServiceTests
+ * Build/Install/Run: atest InputTests:InputManagerServiceTests
*/
@Presubmit
class InputManagerServiceTests {
companion object {
- val ACTION_KEY_EVENTS = listOf(
- KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_LEFT),
- KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_RIGHT),
- KeyEvent( /* downTime= */0, /* eventTime= */0, /* action= */0, /* code= */0,
- /* repeat= */0, KeyEvent.META_META_ON
+ val ACTION_KEY_EVENTS =
+ listOf(
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_LEFT),
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_RIGHT),
+ KeyEvent(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ 0,
+ /* code= */ 0,
+ /* repeat= */ 0,
+ KeyEvent.META_META_ON,
+ ),
)
- )
}
@get:Rule
@@ -108,32 +112,24 @@ class InputManagerServiceTests {
.mockStatic(InputSettings::class.java)
.build()!!
- @get:Rule
- val setFlagsRule = SetFlagsRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
- @get:Rule
- val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
+ @get:Rule val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
- @Mock
- private lateinit var native: NativeInputManagerService
+ @Mock private lateinit var native: NativeInputManagerService
- @Mock
- private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
+ @Mock private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
- @Mock
- private lateinit var windowManagerInternal: WindowManagerInternal
+ @Mock private lateinit var windowManagerInternal: WindowManagerInternal
- @Mock
- private lateinit var packageManagerInternal: PackageManagerInternal
+ @Mock private lateinit var packageManagerInternal: PackageManagerInternal
- @Mock
- private lateinit var uEventManager: UEventManager
+ @Mock private lateinit var uEventManager: UEventManager
@Mock
private lateinit var kbdController: InputManagerService.KeyboardBacklightControllerInterface
- @Mock
- private lateinit var kcm: KeyCharacterMap
+ @Mock private lateinit var kcm: KeyCharacterMap
private lateinit var service: InputManagerService
private lateinit var localService: InputManagerInternal
@@ -147,44 +143,50 @@ class InputManagerServiceTests {
fun setup() {
context = spy(ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()))
fakePermissionEnforcer = FakePermissionEnforcer()
- doReturn(Context.PERMISSION_ENFORCER_SERVICE).`when`(context).getSystemServiceName(
- eq(PermissionEnforcer::class.java)
- )
- doReturn(fakePermissionEnforcer).`when`(context).getSystemService(
- eq(Context.PERMISSION_ENFORCER_SERVICE)
- )
+ doReturn(Context.PERMISSION_ENFORCER_SERVICE)
+ .`when`(context)
+ .getSystemServiceName(eq(PermissionEnforcer::class.java))
+ doReturn(fakePermissionEnforcer)
+ .`when`(context)
+ .getSystemService(eq(Context.PERMISSION_ENFORCER_SERVICE))
contentResolver = MockContentResolver(context)
contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider())
whenever(context.contentResolver).thenReturn(contentResolver)
testLooper = TestLooper()
service =
- InputManagerService(object : InputManagerService.Injector(
- context, testLooper.looper, testLooper.looper, uEventManager) {
- override fun getNativeService(
- service: InputManagerService?
- ): NativeInputManagerService {
- return native
- }
-
- override fun registerLocalService(service: InputManagerInternal?) {
- localService = service!!
- }
-
- override fun getKeyboardBacklightController(
- nativeService: NativeInputManagerService?
- ): InputManagerService.KeyboardBacklightControllerInterface {
- return kbdController
- }
- }, fakePermissionEnforcer)
+ InputManagerService(
+ object :
+ InputManagerService.Injector(
+ context,
+ testLooper.looper,
+ testLooper.looper,
+ uEventManager,
+ ) {
+ override fun getNativeService(
+ service: InputManagerService?
+ ): NativeInputManagerService {
+ return native
+ }
+
+ override fun registerLocalService(service: InputManagerInternal?) {
+ localService = service!!
+ }
+
+ override fun getKeyboardBacklightController(
+ nativeService: NativeInputManagerService?
+ ): InputManagerService.KeyboardBacklightControllerInterface {
+ return kbdController
+ }
+ },
+ fakePermissionEnforcer,
+ )
inputManagerGlobalSession = InputManagerGlobal.createTestSession(service)
val inputManager = InputManager(context)
whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
whenever(context.checkCallingOrSelfPermission(Manifest.permission.MANAGE_KEY_GESTURES))
- .thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
ExtendedMockito.doReturn(windowManagerInternal).`when` {
LocalServices.getService(eq(WindowManagerInternal::class.java))
@@ -192,9 +194,7 @@ class InputManagerServiceTests {
ExtendedMockito.doReturn(packageManagerInternal).`when` {
LocalServices.getService(eq(PackageManagerInternal::class.java))
}
- ExtendedMockito.doReturn(kcm).`when` {
- KeyCharacterMap.load(anyInt())
- }
+ ExtendedMockito.doReturn(kcm).`when` { KeyCharacterMap.load(anyInt()) }
assertTrue("Local service must be registered", this::localService.isInitialized)
service.setWindowManagerCallbacks(wmCallbacks)
@@ -219,9 +219,7 @@ class InputManagerServiceTests {
fun testInputSettingsUpdatedOnSystemRunning() {
verifyNoInteractions(native)
- runWithShellPermissionIdentity {
- service.systemRunning()
- }
+ runWithShellPermissionIdentity { service.systemRunning() }
verify(native).setPointerSpeed(anyInt())
verify(native).setTouchpadPointerSpeed(anyInt())
@@ -238,8 +236,7 @@ class InputManagerServiceTests {
verify(native).setStylusPointerIconEnabled(anyBoolean())
// Called thrice at boot, since there are individual callbacks to update the
// key repeat timeout, the key repeat delay and whether key repeat enabled.
- verify(native, times(3)).setKeyRepeatConfiguration(anyInt(), anyInt(),
- anyBoolean())
+ verify(native, times(3)).setKeyRepeatConfiguration(anyInt(), anyInt(), anyBoolean())
}
@Test
@@ -259,7 +256,9 @@ class InputManagerServiceTests {
localService.setTypeAssociation(inputPort, type)
- assertThat(service.getDeviceTypeAssociations()).asList().containsExactly(inputPort, type)
+ assertThat(service.getDeviceTypeAssociations())
+ .asList()
+ .containsExactly(inputPort, type)
.inOrder()
}
@@ -290,8 +289,8 @@ class InputManagerServiceTests {
fun testActionKeyEventsForwardedToFocusedWindow_whenCorrectlyRequested() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
- /* hasPermission = */true,
- /* hasPrivateFlag = */true
+ /* hasPermission = */ true,
+ /* hasPrivateFlag = */ true,
)
whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
@@ -304,8 +303,8 @@ class InputManagerServiceTests {
fun testActionKeyEventsNotForwardedToFocusedWindow_whenNoPermissions() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
- /* hasPermission = */false,
- /* hasPrivateFlag = */true
+ /* hasPermission = */ false,
+ /* hasPrivateFlag = */ true,
)
whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
@@ -318,8 +317,8 @@ class InputManagerServiceTests {
fun testActionKeyEventsNotForwardedToFocusedWindow_whenNoPrivateFlag() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
- /* hasPermission = */true,
- /* hasPrivateFlag = */false
+ /* hasPermission = */ true,
+ /* hasPrivateFlag = */ false,
)
whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
@@ -362,13 +361,20 @@ class InputManagerServiceTests {
fun testKeyEventsForwardedToFocusedWindow_whenWmAllows() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
- /* hasPermission = */false,
- /* hasPrivateFlag = */false
+ /* hasPermission = */ false,
+ /* hasPrivateFlag = */ false,
)
whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(0)
- val event = KeyEvent( /* downTime= */0, /* eventTime= */0, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_SPACE, /* repeat= */0, KeyEvent.META_CTRL_ON)
+ val event =
+ KeyEvent(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_SPACE,
+ /* repeat= */ 0,
+ KeyEvent.META_CTRL_ON,
+ )
assertEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
}
@@ -399,13 +405,20 @@ class InputManagerServiceTests {
fun testKeyEventsNotForwardedToFocusedWindow_whenWmConsumes() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
- /* hasPermission = */false,
- /* hasPrivateFlag = */false
+ /* hasPermission = */ false,
+ /* hasPrivateFlag = */ false,
)
whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
- val event = KeyEvent( /* downTime= */0, /* eventTime= */0, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_SPACE, /* repeat= */0, KeyEvent.META_CTRL_ON)
+ val event =
+ KeyEvent(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_SPACE,
+ /* repeat= */ 0,
+ KeyEvent.META_CTRL_ON,
+ )
assertEquals(-1, service.interceptKeyBeforeDispatching(null, event, 0))
}
@@ -420,19 +433,20 @@ class InputManagerServiceTests {
}
private fun createVirtualDisplays(count: Int): AutoClosingVirtualDisplays {
- val displayManager: DisplayManager = context.getSystemService(
- DisplayManager::class.java
- ) as DisplayManager
+ val displayManager: DisplayManager =
+ context.getSystemService(DisplayManager::class.java) as DisplayManager
val virtualDisplays = mutableListOf<VirtualDisplay>()
for (i in 0 until count) {
- virtualDisplays.add(displayManager.createVirtualDisplay(
+ virtualDisplays.add(
+ displayManager.createVirtualDisplay(
/* displayName= */ "testVirtualDisplay$i",
/* width= */ 100,
/* height= */ 100,
/* densityDpi= */ 100,
/* surface= */ null,
- /* flags= */ 0
- ))
+ /* flags= */ 0,
+ )
+ )
}
return AutoClosingVirtualDisplays(virtualDisplays)
}
@@ -441,26 +455,26 @@ class InputManagerServiceTests {
private fun createKeycodeAEvent(inputDevice: InputDevice, action: Int): KeyEvent {
val eventTime = SystemClock.uptimeMillis()
return KeyEvent(
- /* downTime= */ eventTime,
- /* eventTime= */ eventTime,
- /* action= */ action,
- /* code= */ KeyEvent.KEYCODE_A,
- /* repeat= */ 0,
- /* metaState= */ 0,
- /* deviceId= */ inputDevice.id,
- /* scanCode= */ 0,
- /* flags= */ KeyEvent.FLAG_FROM_SYSTEM,
- /* source= */ InputDevice.SOURCE_KEYBOARD
+ /* downTime= */ eventTime,
+ /* eventTime= */ eventTime,
+ /* action= */ action,
+ /* code= */ KeyEvent.KEYCODE_A,
+ /* repeat= */ 0,
+ /* metaState= */ 0,
+ /* deviceId= */ inputDevice.id,
+ /* scanCode= */ 0,
+ /* flags= */ KeyEvent.FLAG_FROM_SYSTEM,
+ /* source= */ InputDevice.SOURCE_KEYBOARD,
)
}
private fun createInputDevice(): InputDevice {
return InputDevice.Builder()
- .setId(123)
- .setName("abc")
- .setDescriptor("def")
- .setSources(InputDevice.SOURCE_KEYBOARD)
- .build()
+ .setId(123)
+ .setName("abc")
+ .setDescriptor("def")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .build()
}
@Test
@@ -485,8 +499,8 @@ class InputManagerServiceTests {
// Associate input device with display
service.addUniqueIdAssociationByDescriptor(
- inputDevice.descriptor,
- virtualDisplays[0].display.displayId.toString()
+ inputDevice.descriptor,
+ virtualDisplays[0].display.displayId.toString(),
)
// Simulate 2 different KeyEvents
@@ -513,8 +527,8 @@ class InputManagerServiceTests {
// Associate with Display 2
service.addUniqueIdAssociationByDescriptor(
- inputDevice.descriptor,
- virtualDisplays[1].display.displayId.toString()
+ inputDevice.descriptor,
+ virtualDisplays[1].display.displayId.toString(),
)
// Simulate a KeyEvent
@@ -548,8 +562,8 @@ class InputManagerServiceTests {
// Associate input device with display
service.addUniqueIdAssociationByPort(
- inputDevice.name,
- virtualDisplays[0].display.displayId.toString()
+ inputDevice.name,
+ virtualDisplays[0].display.displayId.toString(),
)
// Simulate 2 different KeyEvents
@@ -576,8 +590,8 @@ class InputManagerServiceTests {
// Associate with Display 2
service.addUniqueIdAssociationByPort(
- inputDevice.name,
- virtualDisplays[1].display.displayId.toString()
+ inputDevice.name,
+ virtualDisplays[1].display.displayId.toString(),
)
// Simulate a KeyEvent
@@ -619,7 +633,7 @@ class InputManagerServiceTests {
ExtendedMockito.verify {
InputSettings.setAccessibilityBounceKeysThreshold(
any(),
- eq(InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS)
+ eq(InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS),
)
}
}
@@ -635,9 +649,7 @@ class InputManagerServiceTests {
.setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
.build()
service.handleKeyGestureEvent(toggleMouseKeysEvent)
- ExtendedMockito.verify {
- InputSettings.setAccessibilityMouseKeysEnabled(any(), eq(true))
- }
+ ExtendedMockito.verify { InputSettings.setAccessibilityMouseKeysEnabled(any(), eq(true)) }
}
@Test
@@ -648,9 +660,7 @@ class InputManagerServiceTests {
.setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
.build()
service.handleKeyGestureEvent(toggleStickyKeysEvent)
- ExtendedMockito.verify {
- InputSettings.setAccessibilityStickyKeysEnabled(any(), eq(true))
- }
+ ExtendedMockito.verify { InputSettings.setAccessibilityStickyKeysEnabled(any(), eq(true)) }
}
@Test
@@ -664,7 +674,7 @@ class InputManagerServiceTests {
ExtendedMockito.verify {
InputSettings.setAccessibilitySlowKeysThreshold(
any(),
- eq(InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS)
+ eq(InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS),
)
}
}
@@ -683,37 +693,39 @@ class InputManagerServiceTests {
fun overrideSendActionKeyEventsToFocusedWindow(
hasPermission: Boolean,
- hasPrivateFlag: Boolean
+ hasPrivateFlag: Boolean,
) {
ExtendedMockito.doReturn(
- if (hasPermission) {
- PermissionChecker.PERMISSION_GRANTED
- } else {
- PermissionChecker.PERMISSION_HARD_DENIED
- }
- ).`when` {
- PermissionChecker.checkPermissionForDataDelivery(
- any(),
- eq(Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW),
- anyInt(),
- anyInt(),
- any(),
- any(),
- any()
+ if (hasPermission) {
+ PermissionChecker.PERMISSION_GRANTED
+ } else {
+ PermissionChecker.PERMISSION_HARD_DENIED
+ }
)
- }
+ .`when` {
+ PermissionChecker.checkPermissionForDataDelivery(
+ any(),
+ eq(Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW),
+ anyInt(),
+ anyInt(),
+ any(),
+ any(),
+ any(),
+ )
+ }
- val info = KeyInterceptionInfo(
- /* type = */0,
- if (hasPrivateFlag) {
- WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS
- } else {
- 0
- },
- "title",
- /* uid = */0,
- /* inputFeatureFlags = */ 0
- )
+ val info =
+ KeyInterceptionInfo(
+ /* type = */ 0,
+ if (hasPrivateFlag) {
+ WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS
+ } else {
+ 0
+ },
+ "title",
+ /* uid = */ 0,
+ /* inputFeatureFlags = */ 0,
+ )
whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
}
}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 163dda84a71c..c64578e4638f 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -77,8 +77,7 @@ import org.mockito.kotlin.times
/**
* Tests for {@link KeyGestureController}.
*
- * Build/Install/Run:
- * atest InputTests:KeyGestureControllerTests
+ * Build/Install/Run: atest InputTests:KeyGestureControllerTests
*/
@Presubmit
@RunWith(JUnitParamsRunner::class)
@@ -86,23 +85,29 @@ class KeyGestureControllerTests {
companion object {
const val DEVICE_ID = 1
- val HOME_GESTURE_COMPLETE_EVENT = KeyGestureEvent.Builder()
- .setDeviceId(DEVICE_ID)
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
- .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
- .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- .build()
- val MODIFIER = mapOf(
- KeyEvent.KEYCODE_CTRL_LEFT to (KeyEvent.META_CTRL_LEFT_ON or KeyEvent.META_CTRL_ON),
- KeyEvent.KEYCODE_CTRL_RIGHT to (KeyEvent.META_CTRL_RIGHT_ON or KeyEvent.META_CTRL_ON),
- KeyEvent.KEYCODE_ALT_LEFT to (KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON),
- KeyEvent.KEYCODE_ALT_RIGHT to (KeyEvent.META_ALT_RIGHT_ON or KeyEvent.META_ALT_ON),
- KeyEvent.KEYCODE_SHIFT_LEFT to (KeyEvent.META_SHIFT_LEFT_ON or KeyEvent.META_SHIFT_ON),
- KeyEvent.KEYCODE_SHIFT_RIGHT to (KeyEvent.META_SHIFT_RIGHT_ON or KeyEvent.META_SHIFT_ON),
- KeyEvent.KEYCODE_META_LEFT to (KeyEvent.META_META_LEFT_ON or KeyEvent.META_META_ON),
- KeyEvent.KEYCODE_META_RIGHT to (KeyEvent.META_META_RIGHT_ON or KeyEvent.META_META_ON),
- )
+ val HOME_GESTURE_COMPLETE_EVENT =
+ KeyGestureEvent.Builder()
+ .setDeviceId(DEVICE_ID)
+ .setKeycodes(intArrayOf(KeyEvent.KEYCODE_H))
+ .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON)
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .build()
+ val MODIFIER =
+ mapOf(
+ KeyEvent.KEYCODE_CTRL_LEFT to (KeyEvent.META_CTRL_LEFT_ON or KeyEvent.META_CTRL_ON),
+ KeyEvent.KEYCODE_CTRL_RIGHT to
+ (KeyEvent.META_CTRL_RIGHT_ON or KeyEvent.META_CTRL_ON),
+ KeyEvent.KEYCODE_ALT_LEFT to (KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON),
+ KeyEvent.KEYCODE_ALT_RIGHT to (KeyEvent.META_ALT_RIGHT_ON or KeyEvent.META_ALT_ON),
+ KeyEvent.KEYCODE_SHIFT_LEFT to
+ (KeyEvent.META_SHIFT_LEFT_ON or KeyEvent.META_SHIFT_ON),
+ KeyEvent.KEYCODE_SHIFT_RIGHT to
+ (KeyEvent.META_SHIFT_RIGHT_ON or KeyEvent.META_SHIFT_ON),
+ KeyEvent.KEYCODE_META_LEFT to (KeyEvent.META_META_LEFT_ON or KeyEvent.META_META_ON),
+ KeyEvent.KEYCODE_META_RIGHT to
+ (KeyEvent.META_META_RIGHT_ON or KeyEvent.META_META_ON),
+ )
const val SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH = 0
const val SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY = 1
const val SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0
@@ -116,15 +121,14 @@ class KeyGestureControllerTests {
@JvmField
@Rule
- val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
- .mockStatic(FrameworkStatsLog::class.java)
- .mockStatic(SystemProperties::class.java)
- .mockStatic(KeyCharacterMap::class.java)
- .build()!!
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(FrameworkStatsLog::class.java)
+ .mockStatic(SystemProperties::class.java)
+ .mockStatic(KeyCharacterMap::class.java)
+ .build()!!
- @JvmField
- @Rule
- val rule = SetFlagsRule()
+ @JvmField @Rule val rule = SetFlagsRule()
@Mock private lateinit var iInputManager: IInputManager
@Mock private lateinit var packageManager: PackageManager
@@ -151,29 +155,35 @@ class KeyGestureControllerTests {
currentPid = Process.myPid()
tempFile = File.createTempFile("input_gestures", ".xml")
inputDataStore =
- InputDataStore(object : InputDataStore.FileInjector("input_gestures.xml") {
- private val atomicFile: AtomicFile = AtomicFile(tempFile)
+ InputDataStore(
+ object : InputDataStore.FileInjector("input_gestures.xml") {
+ private val atomicFile: AtomicFile = AtomicFile(tempFile)
- override fun openRead(userId: Int): InputStream? {
- return atomicFile.openRead()
- }
+ override fun openRead(userId: Int): InputStream? {
+ return atomicFile.openRead()
+ }
- override fun startWrite(userId: Int): FileOutputStream? {
- return atomicFile.startWrite()
- }
+ override fun startWrite(userId: Int): FileOutputStream? {
+ return atomicFile.startWrite()
+ }
- override fun finishWrite(userId: Int, fos: FileOutputStream?, success: Boolean) {
- if (success) {
- atomicFile.finishWrite(fos)
- } else {
- atomicFile.failWrite(fos)
+ override fun finishWrite(
+ userId: Int,
+ fos: FileOutputStream?,
+ success: Boolean,
+ ) {
+ if (success) {
+ atomicFile.finishWrite(fos)
+ } else {
+ atomicFile.failWrite(fos)
+ }
}
- }
- override fun getAtomicFileForUserId(userId: Int): AtomicFile {
- return atomicFile
+ override fun getAtomicFileForUserId(userId: Int): AtomicFile {
+ return atomicFile
+ }
}
- })
+ )
startNewInputGlobalTestSession()
}
@@ -230,11 +240,12 @@ class KeyGestureControllerTests {
object : KeyGestureController.Injector() {
override fun getAccessibilityShortcutController(
context: Context?,
- handler: Handler?
+ handler: Handler?,
): AccessibilityShortcutController {
return accessibilityShortcutController
}
- })
+ },
+ )
Mockito.`when`(iInputManager.registerKeyGestureHandler(Mockito.any(), Mockito.any()))
.thenAnswer {
val args = it.arguments
@@ -242,14 +253,18 @@ class KeyGestureControllerTests {
keyGestureController.registerKeyGestureHandler(
args[0] as IntArray,
args[1] as IKeyGestureHandler,
- SYSTEM_PID
+ SYSTEM_PID,
)
}
- }
+ }
keyGestureController.setWindowManagerCallbacks(wmCallbacks)
Mockito.`when`(wmCallbacks.isKeyguardLocked(Mockito.anyInt())).thenReturn(false)
- Mockito.`when`(accessibilityShortcutController
- .isAccessibilityShortcutAvailable(Mockito.anyBoolean())).thenReturn(true)
+ Mockito.`when`(
+ accessibilityShortcutController.isAccessibilityShortcutAvailable(
+ Mockito.anyBoolean()
+ )
+ )
+ .thenReturn(true)
Mockito.`when`(iInputManager.appLaunchBookmarks)
.thenReturn(keyGestureController.appLaunchBookmarks)
keyGestureController.systemRunning()
@@ -258,9 +273,10 @@ class KeyGestureControllerTests {
private fun notifyHomeGestureCompleted() {
keyGestureController.notifyKeyGestureCompleted(
- DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H),
+ DEVICE_ID,
+ intArrayOf(KeyEvent.KEYCODE_H),
KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
)
}
@@ -273,15 +289,11 @@ class KeyGestureControllerTests {
keyGestureController.registerKeyGestureEventListener(listener, 0)
notifyHomeGestureCompleted()
testLooper.dispatchAll()
- assertEquals(
- "Listener should get callbacks on key gesture event completed",
- 1,
- events.size
- )
+ assertEquals("Listener should get callbacks on key gesture event completed", 1, events.size)
assertEquals(
"Listener should get callback for key gesture complete event",
HOME_GESTURE_COMPLETE_EVENT,
- events[0]
+ events[0],
)
// Unregister listener
@@ -289,11 +301,7 @@ class KeyGestureControllerTests {
keyGestureController.unregisterKeyGestureEventListener(listener, 0)
notifyHomeGestureCompleted()
testLooper.dispatchAll()
- assertEquals(
- "Listener should not get callback after being unregistered",
- 0,
- events.size
- )
+ assertEquals("Listener should not get callback after being unregistered", 0, events.size)
}
class TestData(
@@ -317,7 +325,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
intArrayOf(KeyEvent.KEYCODE_A),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + H -> Go Home",
@@ -325,7 +333,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
intArrayOf(KeyEvent.KEYCODE_H),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ENTER -> Go Home",
@@ -333,7 +341,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
intArrayOf(KeyEvent.KEYCODE_ENTER),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + I -> Launch System Settings",
@@ -341,7 +349,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
intArrayOf(KeyEvent.KEYCODE_I),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + L -> Lock",
@@ -349,7 +357,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
intArrayOf(KeyEvent.KEYCODE_L),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + N -> Toggle Notification",
@@ -357,18 +365,15 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
intArrayOf(KeyEvent.KEYCODE_N),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + S -> Take Screenshot",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_S
- ),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
intArrayOf(KeyEvent.KEYCODE_S),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ESC -> Back",
@@ -376,7 +381,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
intArrayOf(KeyEvent.KEYCODE_ESCAPE),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + DPAD_LEFT -> Back",
@@ -384,55 +389,55 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + CTRL + DPAD_UP -> Multi Window Navigation",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DPAD_UP
+ KeyEvent.KEYCODE_DPAD_UP,
),
KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + CTRL + DPAD_DOWN -> Desktop Mode",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DPAD_DOWN
+ KeyEvent.KEYCODE_DPAD_DOWN,
),
KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + CTRL + DPAD_LEFT -> Splitscreen Navigation Left",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT
+ KeyEvent.KEYCODE_DPAD_LEFT,
),
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + CTRL + DPAD_RIGHT -> Splitscreen Navigation Right",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DPAD_RIGHT
+ KeyEvent.KEYCODE_DPAD_RIGHT,
),
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + / -> Open Shortcut Helper",
@@ -440,7 +445,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
intArrayOf(KeyEvent.KEYCODE_SLASH),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ALT -> Toggle Caps Lock",
@@ -448,7 +453,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"ALT + META -> Toggle Caps Lock",
@@ -456,7 +461,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + TAB -> Open Overview",
@@ -464,7 +469,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
intArrayOf(KeyEvent.KEYCODE_TAB),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"ALT + TAB -> Toggle Recent Apps Switcher",
@@ -474,8 +479,8 @@ class KeyGestureControllerTests {
KeyEvent.META_ALT_ON,
intArrayOf(
KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ ),
),
TestData(
"CTRL + SPACE -> Switch Language Forward",
@@ -483,31 +488,31 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
intArrayOf(KeyEvent.KEYCODE_SPACE),
KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"CTRL + SHIFT + SPACE -> Switch Language Backward",
intArrayOf(
KeyEvent.KEYCODE_CTRL_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_SPACE
+ KeyEvent.KEYCODE_SPACE,
),
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
intArrayOf(KeyEvent.KEYCODE_SPACE),
KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"CTRL + ALT + Z -> Accessibility Shortcut",
intArrayOf(
KeyEvent.KEYCODE_CTRL_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_Z
+ KeyEvent.KEYCODE_Z,
),
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
intArrayOf(KeyEvent.KEYCODE_Z),
KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + B -> Launch Default Browser",
@@ -516,7 +521,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_B),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER),
),
TestData(
"META + C -> Launch Default Contacts",
@@ -525,7 +530,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS),
),
TestData(
"META + E -> Launch Default Email",
@@ -534,7 +539,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_E),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL),
),
TestData(
"META + K -> Launch Default Calendar",
@@ -543,7 +548,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR),
),
TestData(
"META + M -> Launch Default Maps",
@@ -552,7 +557,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_M),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS),
),
TestData(
"META + U -> Launch Default Calculator",
@@ -561,159 +566,147 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_U),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR),
),
TestData(
"META + CTRL + DEL -> Trigger Bug Report",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DEL
+ KeyEvent.KEYCODE_DEL,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
intArrayOf(KeyEvent.KEYCODE_DEL),
KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"Meta + Alt + 3 -> Toggle Bounce Keys",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_3
+ KeyEvent.KEYCODE_3,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
intArrayOf(KeyEvent.KEYCODE_3),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"Meta + Alt + 4 -> Toggle Mouse Keys",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_4
+ KeyEvent.KEYCODE_4,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
intArrayOf(KeyEvent.KEYCODE_4),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"Meta + Alt + 5 -> Toggle Sticky Keys",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_5
+ KeyEvent.KEYCODE_5,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
intArrayOf(KeyEvent.KEYCODE_5),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"Meta + Alt + 6 -> Toggle Slow Keys",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_6
+ KeyEvent.KEYCODE_6,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
intArrayOf(KeyEvent.KEYCODE_6),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + CTRL + D -> Move a task to next display",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_D
+ KeyEvent.KEYCODE_D,
),
KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
intArrayOf(KeyEvent.KEYCODE_D),
KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + [ -> Resizes a task to fit the left half of the screen",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_LEFT_BRACKET
- ),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_LEFT_BRACKET),
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ] -> Resizes a task to fit the right half of the screen",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_RIGHT_BRACKET
- ),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_RIGHT_BRACKET),
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + '=' -> Toggles maximization of a task to maximized and restore its bounds",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_EQUALS
- ),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_EQUALS),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_EQUALS),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + '-' -> Minimizes a freeform task",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_MINUS
- ),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_MINUS),
KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW,
intArrayOf(KeyEvent.KEYCODE_MINUS),
KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ALT + M -> Toggle Magnification",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_M
+ KeyEvent.KEYCODE_M,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
intArrayOf(KeyEvent.KEYCODE_M),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ALT + S -> Activate Select to Speak",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_S
+ KeyEvent.KEYCODE_S,
),
KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
intArrayOf(KeyEvent.KEYCODE_S),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ALT + 'V' -> Toggle Voice Access",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_V
+ KeyEvent.KEYCODE_V,
),
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS,
intArrayOf(KeyEvent.KEYCODE_V),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
)
}
@@ -727,7 +720,7 @@ class KeyGestureControllerTests {
com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES,
com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES,
com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
- com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS,
)
fun testKeyGestures(test: TestData) {
setupKeyGestureController()
@@ -743,26 +736,27 @@ class KeyGestureControllerTests {
com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES,
com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES,
com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
- com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS,
)
fun testCustomKeyGesturesNotAllowedForSystemGestures(test: TestData) {
setupKeyGestureController()
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(
- InputGestureData.createKeyTrigger(
- test.expectedKeys[0],
- test.expectedModifierState
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ test.expectedKeys[0],
+ test.expectedModifierState,
+ )
)
- )
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
assertEquals(
test.toString(),
InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE,
- keyGestureController.addCustomInputGesture(0, builder.build().aidlData)
+ keyGestureController.addCustomInputGesture(0, builder.build().aidlData),
)
}
@@ -776,7 +770,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_B),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER),
),
TestData(
"META + P -> Launch Default Contacts",
@@ -785,7 +779,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS),
),
TestData(
"META + E -> Launch Default Email",
@@ -794,7 +788,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_E),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL),
),
TestData(
"META + C -> Launch Default Calendar",
@@ -803,7 +797,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR),
),
TestData(
"META + M -> Launch Default Maps",
@@ -812,7 +806,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_M),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS),
),
TestData(
"META + U -> Launch Default Calculator",
@@ -821,47 +815,47 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_U),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR),
),
TestData(
"META + SHIFT + B -> Launch Default Browser",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_B
+ KeyEvent.KEYCODE_B,
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
intArrayOf(KeyEvent.KEYCODE_B),
KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER),
),
TestData(
"META + SHIFT + P -> Launch Default Contacts",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_P
+ KeyEvent.KEYCODE_P,
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS),
),
TestData(
"META + SHIFT + J -> Launch Target Activity",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_J
+ KeyEvent.KEYCODE_J,
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
intArrayOf(KeyEvent.KEYCODE_J),
KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
- )
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest"),
+ ),
)
}
@@ -890,7 +884,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"APP_SWITCH -> App Switch",
@@ -900,8 +894,8 @@ class KeyGestureControllerTests {
0,
intArrayOf(
KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ ),
),
TestData(
"BRIGHTNESS_UP -> Brightness Up",
@@ -909,7 +903,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"BRIGHTNESS_DOWN -> Brightness Down",
@@ -917,7 +911,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"KEYBOARD_BACKLIGHT_UP -> Keyboard Backlight Up",
@@ -925,7 +919,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"KEYBOARD_BACKLIGHT_DOWN -> Keyboard Backlight Down",
@@ -933,7 +927,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"KEYBOARD_BACKLIGHT_TOGGLE -> Keyboard Backlight Toggle",
@@ -941,7 +935,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"ALL_APPS -> Open App Drawer",
@@ -949,7 +943,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
intArrayOf(KeyEvent.KEYCODE_ALL_APPS),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"NOTIFICATION -> Toggle Notification Panel",
@@ -957,7 +951,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
intArrayOf(KeyEvent.KEYCODE_NOTIFICATION),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"LANGUAGE_SWITCH -> Switch Language Forward",
@@ -965,7 +959,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"SHIFT + LANGUAGE_SWITCH -> Switch Language Backward",
@@ -973,7 +967,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
KeyEvent.META_SHIFT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"SCREENSHOT -> Take Screenshot",
@@ -981,7 +975,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
intArrayOf(KeyEvent.KEYCODE_SCREENSHOT),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META -> Open Apps Drawer",
@@ -989,7 +983,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
intArrayOf(KeyEvent.KEYCODE_META_LEFT),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"SYSRQ -> Take screenshot",
@@ -997,7 +991,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
intArrayOf(KeyEvent.KEYCODE_SYSRQ),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"ESC -> Close All Dialogs",
@@ -1005,7 +999,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
intArrayOf(KeyEvent.KEYCODE_ESCAPE),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"EXPLORER -> Launch Default Browser",
@@ -1014,7 +1008,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_EXPLORER),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER),
),
TestData(
"ENVELOPE -> Launch Default Email",
@@ -1023,7 +1017,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_ENVELOPE),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL),
),
TestData(
"CONTACTS -> Launch Default Contacts",
@@ -1032,7 +1026,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_CONTACTS),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS),
),
TestData(
"CALENDAR -> Launch Default Calendar",
@@ -1041,7 +1035,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_CALENDAR),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR),
),
TestData(
"MUSIC -> Launch Default Music",
@@ -1050,7 +1044,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_MUSIC),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC),
),
TestData(
"CALCULATOR -> Launch Default Calculator",
@@ -1059,7 +1053,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_CALCULATOR),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR),
),
TestData(
"LOCK -> Lock Screen",
@@ -1067,7 +1061,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
intArrayOf(KeyEvent.KEYCODE_LOCK),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"FULLSCREEN -> Turns a task into fullscreen",
@@ -1075,7 +1069,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
intArrayOf(KeyEvent.KEYCODE_FULLSCREEN),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
)
}
@@ -1091,31 +1085,32 @@ class KeyGestureControllerTests {
@Test
fun testKeycodesFullyConsumed_irrespectiveOfHandlers() {
setupKeyGestureController()
- val testKeys = intArrayOf(
- KeyEvent.KEYCODE_RECENT_APPS,
- KeyEvent.KEYCODE_APP_SWITCH,
- KeyEvent.KEYCODE_BRIGHTNESS_UP,
- KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
- KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
- KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP,
- KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
- KeyEvent.KEYCODE_ALL_APPS,
- KeyEvent.KEYCODE_NOTIFICATION,
- KeyEvent.KEYCODE_SETTINGS,
- KeyEvent.KEYCODE_LANGUAGE_SWITCH,
- KeyEvent.KEYCODE_SCREENSHOT,
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_META_RIGHT,
- KeyEvent.KEYCODE_ASSIST,
- KeyEvent.KEYCODE_VOICE_ASSIST,
- KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY,
- KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
- KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
- KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
- KeyEvent.KEYCODE_DO_NOT_DISTURB,
- KeyEvent.KEYCODE_LOCK,
- KeyEvent.KEYCODE_FULLSCREEN
- )
+ val testKeys =
+ intArrayOf(
+ KeyEvent.KEYCODE_RECENT_APPS,
+ KeyEvent.KEYCODE_APP_SWITCH,
+ KeyEvent.KEYCODE_BRIGHTNESS_UP,
+ KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
+ KeyEvent.KEYCODE_ALL_APPS,
+ KeyEvent.KEYCODE_NOTIFICATION,
+ KeyEvent.KEYCODE_SETTINGS,
+ KeyEvent.KEYCODE_LANGUAGE_SWITCH,
+ KeyEvent.KEYCODE_SCREENSHOT,
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_META_RIGHT,
+ KeyEvent.KEYCODE_ASSIST,
+ KeyEvent.KEYCODE_VOICE_ASSIST,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
+ KeyEvent.KEYCODE_DO_NOT_DISTURB,
+ KeyEvent.KEYCODE_LOCK,
+ KeyEvent.KEYCODE_FULLSCREEN,
+ )
for (key in testKeys) {
sendKeys(intArrayOf(key), assertNotSentToApps = true)
@@ -1130,7 +1125,7 @@ class KeyGestureControllerTests {
testKeyGestureNotProduced(
"SEARCH -> Default Search",
intArrayOf(KeyEvent.KEYCODE_SEARCH),
- intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH)
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH),
)
}
@@ -1146,7 +1141,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
intArrayOf(KeyEvent.KEYCODE_SEARCH),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
)
)
}
@@ -1161,8 +1156,8 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
intArrayOf(
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
- )
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ ),
)
}
@@ -1178,7 +1173,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
)
)
}
@@ -1195,7 +1190,7 @@ class KeyGestureControllerTests {
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
)
)
}
@@ -1208,15 +1203,11 @@ class KeyGestureControllerTests {
keyGestureController.registerKeyGestureEventListener(listener, 0)
sendKeys(intArrayOf(KeyEvent.KEYCODE_CAPS_LOCK))
testLooper.dispatchAll()
- assertEquals(
- "Listener should get callbacks on key gesture event completed",
- 1,
- events.size
- )
+ assertEquals("Listener should get callbacks on key gesture event completed", 1, events.size)
assertEquals(
"Listener should get callback for Toggle Caps Lock key gesture complete event",
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
- events[0].keyGestureType
+ events[0].keyGestureType,
)
}
@@ -1231,8 +1222,8 @@ class KeyGestureControllerTests {
0,
intArrayOf(
KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ ),
),
TestData(
"POWER + STEM_PRIMARY -> Screenshot Chord",
@@ -1242,8 +1233,8 @@ class KeyGestureControllerTests {
0,
intArrayOf(
KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ ),
),
TestData(
"BACK + DPAD_CENTER -> TV Trigger Bug Report",
@@ -1253,8 +1244,8 @@ class KeyGestureControllerTests {
0,
intArrayOf(
KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ ),
),
)
}
@@ -1263,7 +1254,7 @@ class KeyGestureControllerTests {
@Parameters(method = "systemGesturesTestArguments_forKeyCombinations")
@EnableFlags(
com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
- com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_KEY_GESTURES
+ com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_KEY_GESTURES,
)
fun testKeyCombinationGestures(test: TestData) {
setupKeyGestureController()
@@ -1278,29 +1269,25 @@ class KeyGestureControllerTests {
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_Q
+ KeyEvent.KEYCODE_Q,
),
KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
intArrayOf(KeyEvent.KEYCODE_Q),
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
),
TestData(
"META + ALT + Q -> Launch app",
intArrayOf(
KeyEvent.KEYCODE_CTRL_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_Q
+ KeyEvent.KEYCODE_Q,
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
intArrayOf(KeyEvent.KEYCODE_Q),
KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
- intArrayOf(
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- ),
- AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest"),
),
)
}
@@ -1309,31 +1296,27 @@ class KeyGestureControllerTests {
@Parameters(method = "customInputGesturesTestArguments")
fun testCustomKeyGestures(test: TestData) {
setupKeyGestureController()
- val trigger = InputGestureData.createKeyTrigger(
- test.expectedKeys[0],
- test.expectedModifierState
- )
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(trigger)
+ val trigger =
+ InputGestureData.createKeyTrigger(test.expectedKeys[0], test.expectedModifierState)
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(trigger)
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
val inputGestureData = builder.build()
- assertNull(
- test.toString(),
- keyGestureController.getInputGesture(0, trigger.aidlTrigger)
- )
+ assertNull(test.toString(), keyGestureController.getInputGesture(0, trigger.aidlTrigger))
assertEquals(
test.toString(),
InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS,
- keyGestureController.addCustomInputGesture(0, builder.build().aidlData)
+ keyGestureController.addCustomInputGesture(0, builder.build().aidlData),
)
assertEquals(
test.toString(),
inputGestureData.aidlData,
- keyGestureController.getInputGesture(0, trigger.aidlTrigger)
+ keyGestureController.getInputGesture(0, trigger.aidlTrigger),
)
testKeyGestureInternal(test)
}
@@ -1343,14 +1326,15 @@ class KeyGestureControllerTests {
fun testCustomKeyGesturesSavedAndLoadedByController(test: TestData) {
val userId = 10
setupKeyGestureController()
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(
- InputGestureData.createKeyTrigger(
- test.expectedKeys[0],
- test.expectedModifierState
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ test.expectedKeys[0],
+ test.expectedModifierState,
+ )
)
- )
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
@@ -1371,11 +1355,12 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
1,
- savedInputGestures.size
+ savedInputGestures.size,
)
assertEquals(
- "Test: $test doesn't produce correct input gesture data", inputGestureData,
- InputGestureData(savedInputGestures[0])
+ "Test: $test doesn't produce correct input gesture data",
+ inputGestureData,
+ InputGestureData(savedInputGestures[0]),
)
}
@@ -1384,14 +1369,15 @@ class KeyGestureControllerTests {
fun testCustomKeyGestureRestoredFromBackup(test: TestData) {
val userId = 10
setupKeyGestureController()
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(
- InputGestureData.createKeyTrigger(
- test.expectedKeys[0],
- test.expectedModifierState
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ test.expectedKeys[0],
+ test.expectedModifierState,
+ )
)
- )
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
@@ -1415,7 +1401,7 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
0,
- savedInputGestures.size
+ savedInputGestures.size,
)
// After the restore, there should be the original gesture re-registered.
@@ -1424,11 +1410,12 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
1,
- savedInputGestures.size
+ savedInputGestures.size,
)
assertEquals(
- "Test: $test doesn't produce correct input gesture data", inputGestureData,
- InputGestureData(savedInputGestures[0])
+ "Test: $test doesn't produce correct input gesture data",
+ inputGestureData,
+ InputGestureData(savedInputGestures[0]),
)
}
@@ -1449,14 +1436,14 @@ class KeyGestureControllerTests {
"3 Finger Tap -> Go Home",
InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP,
KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
),
TouchpadTestData(
"3 Finger Tap -> Launch app",
InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest"),
),
)
}
@@ -1465,9 +1452,10 @@ class KeyGestureControllerTests {
@Parameters(method = "customTouchpadGesturesTestArguments")
fun testCustomTouchpadGesture(test: TouchpadTestData) {
setupKeyGestureController()
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
@@ -1476,13 +1464,11 @@ class KeyGestureControllerTests {
keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData)
val handledEvents = mutableListOf<KeyGestureEvent>()
- val handler = KeyGestureHandler { event, _ ->
- handledEvents.add(KeyGestureEvent(event))
- }
+ val handler = KeyGestureHandler { event, _ -> handledEvents.add(KeyGestureEvent(event)) }
keyGestureController.registerKeyGestureHandler(
intArrayOf(test.expectedKeyGestureType),
handler,
- TEST_PID
+ TEST_PID,
)
handledEvents.clear()
@@ -1491,23 +1477,23 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of key gesture events",
1,
- handledEvents.size
+ handledEvents.size,
)
val event = handledEvents[0]
assertEquals(
"Test: $test doesn't produce correct key gesture type",
test.expectedKeyGestureType,
- event.keyGestureType
+ event.keyGestureType,
)
assertEquals(
"Test: $test doesn't produce correct key gesture action",
test.expectedAction,
- event.action
+ event.action,
)
assertEquals(
"Test: $test doesn't produce correct app launch data",
test.expectedAppLaunchData,
- event.appLaunchData
+ event.appLaunchData,
)
keyGestureController.unregisterKeyGestureHandler(handler, TEST_PID)
@@ -1518,9 +1504,10 @@ class KeyGestureControllerTests {
fun testCustomTouchpadGesturesSavedAndLoadedByController(test: TouchpadTestData) {
val userId = 10
setupKeyGestureController()
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
@@ -1540,23 +1527,24 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
1,
- savedInputGestures.size
+ savedInputGestures.size,
)
assertEquals(
- "Test: $test doesn't produce correct input gesture data", inputGestureData,
- InputGestureData(savedInputGestures[0])
+ "Test: $test doesn't produce correct input gesture data",
+ inputGestureData,
+ InputGestureData(savedInputGestures[0]),
)
}
-
@Test
@Parameters(method = "customTouchpadGesturesTestArguments")
fun testCustomTouchpadGesturesRestoredFromBackup(test: TouchpadTestData) {
val userId = 10
setupKeyGestureController()
- val builder = InputGestureData.Builder()
- .setKeyGestureType(test.expectedKeyGestureType)
- .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ val builder =
+ InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
@@ -1579,7 +1567,7 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
0,
- savedInputGestures.size
+ savedInputGestures.size,
)
// After the restore, there should be the original gesture re-registered.
@@ -1588,11 +1576,12 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
1,
- savedInputGestures.size
+ savedInputGestures.size,
)
assertEquals(
- "Test: $test doesn't produce correct input gesture data", inputGestureData,
- InputGestureData(savedInputGestures[0])
+ "Test: $test doesn't produce correct input gesture data",
+ inputGestureData,
+ InputGestureData(savedInputGestures[0]),
)
}
@@ -1604,7 +1593,7 @@ class KeyGestureControllerTests {
intArrayOf(KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN),
// Assuming this value is always greater than the accessibility shortcut timeout, which
// currently defaults to 3000ms
- timeDelayMs = 10000
+ timeDelayMs = 10000,
)
Mockito.verify(accessibilityShortcutController, times(1)).performAccessibilityShortcut()
}
@@ -1613,10 +1602,7 @@ class KeyGestureControllerTests {
fun testAccessibilityTvShortcutChordPressed() {
setupKeyGestureController()
- sendKeys(
- intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
- timeDelayMs = 10000
- )
+ sendKeys(intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN), timeDelayMs = 10000)
Mockito.verify(accessibilityShortcutController, times(1)).performAccessibilityShortcut()
}
@@ -1626,7 +1612,7 @@ class KeyGestureControllerTests {
sendKeys(
intArrayOf(KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN),
- timeDelayMs = 0
+ timeDelayMs = 0,
)
Mockito.verify(accessibilityShortcutController, never()).performAccessibilityShortcut()
}
@@ -1635,10 +1621,7 @@ class KeyGestureControllerTests {
fun testAccessibilityTvShortcutChordPressedForLessThanTimeout() {
setupKeyGestureController()
- sendKeys(
- intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
- timeDelayMs = 0
- )
+ sendKeys(intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN), timeDelayMs = 0)
Mockito.verify(accessibilityShortcutController, never()).performAccessibilityShortcut()
}
@@ -1651,14 +1634,14 @@ class KeyGestureControllerTests {
keyGestureController.registerKeyGestureHandler(
intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
handler1,
- RANDOM_PID1
+ RANDOM_PID1,
)
assertThrows(IllegalStateException::class.java) {
keyGestureController.registerKeyGestureHandler(
intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
handler2,
- RANDOM_PID1
+ RANDOM_PID1,
)
}
}
@@ -1672,14 +1655,14 @@ class KeyGestureControllerTests {
keyGestureController.registerKeyGestureHandler(
intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
handler1,
- RANDOM_PID1
+ RANDOM_PID1,
)
assertThrows(IllegalArgumentException::class.java) {
keyGestureController.registerKeyGestureHandler(
intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
handler2,
- RANDOM_PID2
+ RANDOM_PID2,
)
}
}
@@ -1691,11 +1674,7 @@ class KeyGestureControllerTests {
val handler = KeyGestureHandler { _, _ -> }
assertThrows(IllegalArgumentException::class.java) {
- keyGestureController.registerKeyGestureHandler(
- intArrayOf(),
- handler,
- RANDOM_PID1
- )
+ keyGestureController.registerKeyGestureHandler(intArrayOf(), handler, RANDOM_PID1)
}
}
@@ -1708,15 +1687,12 @@ class KeyGestureControllerTests {
keyGestureController.registerKeyGestureHandler(
intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
handler1,
- TEST_PID
+ TEST_PID,
)
sendKeys(intArrayOf(KeyEvent.KEYCODE_RECENT_APPS))
assertEquals(1, callbackCount)
- keyGestureController.unregisterKeyGestureHandler(
- handler1,
- TEST_PID
- )
+ keyGestureController.unregisterKeyGestureHandler(handler1, TEST_PID)
// Callback should not be sent after unregister
sendKeys(intArrayOf(KeyEvent.KEYCODE_RECENT_APPS))
@@ -1725,13 +1701,11 @@ class KeyGestureControllerTests {
private fun testKeyGestureInternal(test: TestData) {
val handledEvents = mutableListOf<KeyGestureEvent>()
- val handler = KeyGestureHandler { event, _ ->
- handledEvents.add(KeyGestureEvent(event))
- }
+ val handler = KeyGestureHandler { event, _ -> handledEvents.add(KeyGestureEvent(event)) }
keyGestureController.registerKeyGestureHandler(
intArrayOf(test.expectedKeyGestureType),
handler,
- TEST_PID
+ TEST_PID,
)
handledEvents.clear()
@@ -1740,34 +1714,34 @@ class KeyGestureControllerTests {
assertEquals(
"Test: $test doesn't produce correct number of key gesture events",
test.expectedActions.size,
- handledEvents.size
+ handledEvents.size,
)
for (i in handledEvents.indices) {
val event = handledEvents[i]
assertArrayEquals(
"Test: $test doesn't produce correct key gesture keycodes",
test.expectedKeys,
- event.keycodes
+ event.keycodes,
)
assertEquals(
"Test: $test doesn't produce correct key gesture modifier state",
test.expectedModifierState,
- event.modifierState
+ event.modifierState,
)
assertEquals(
"Test: $test doesn't produce correct key gesture type",
test.expectedKeyGestureType,
- event.keyGestureType
+ event.keyGestureType,
)
assertEquals(
"Test: $test doesn't produce correct key gesture action",
test.expectedActions[i],
- event.action
+ event.action,
)
assertEquals(
"Test: $test doesn't produce correct app launch data",
test.expectedAppLaunchData,
- event.appLaunchData
+ event.appLaunchData,
)
}
@@ -1777,12 +1751,10 @@ class KeyGestureControllerTests {
private fun testKeyGestureNotProduced(
testName: String,
testKeys: IntArray,
- possibleGestures: IntArray
+ possibleGestures: IntArray,
) {
var handledEvents = mutableListOf<KeyGestureEvent>()
- val handler = KeyGestureHandler { event, _ ->
- handledEvents.add(KeyGestureEvent(event))
- }
+ val handler = KeyGestureHandler { event, _ -> handledEvents.add(KeyGestureEvent(event)) }
keyGestureController.registerKeyGestureHandler(possibleGestures, handler, TEST_PID)
handledEvents.clear()
@@ -1793,16 +1765,24 @@ class KeyGestureControllerTests {
private fun sendKeys(
testKeys: IntArray,
assertNotSentToApps: Boolean = false,
- timeDelayMs: Long = 0
+ timeDelayMs: Long = 0,
) {
var metaState = 0
val now = SystemClock.uptimeMillis()
for (key in testKeys) {
- val downEvent = KeyEvent(
- now, now, KeyEvent.ACTION_DOWN, key, 0 /*repeat*/, metaState,
- DEVICE_ID, 0 /*scancode*/, 0 /*flags*/,
- InputDevice.SOURCE_KEYBOARD
- )
+ val downEvent =
+ KeyEvent(
+ now,
+ now,
+ KeyEvent.ACTION_DOWN,
+ key,
+ 0 /*repeat*/,
+ metaState,
+ DEVICE_ID,
+ 0 /*scancode*/,
+ 0 /*flags*/,
+ InputDevice.SOURCE_KEYBOARD,
+ )
interceptKey(downEvent, assertNotSentToApps)
metaState = metaState or MODIFIER.getOrDefault(key, 0)
@@ -1816,11 +1796,19 @@ class KeyGestureControllerTests {
}
for (key in testKeys.reversed()) {
- val upEvent = KeyEvent(
- now, now, KeyEvent.ACTION_UP, key, 0 /*repeat*/, metaState,
- DEVICE_ID, 0 /*scancode*/, 0 /*flags*/,
- InputDevice.SOURCE_KEYBOARD
- )
+ val upEvent =
+ KeyEvent(
+ now,
+ now,
+ KeyEvent.ACTION_UP,
+ key,
+ 0 /*repeat*/,
+ metaState,
+ DEVICE_ID,
+ 0 /*scancode*/,
+ 0 /*flags*/,
+ InputDevice.SOURCE_KEYBOARD,
+ )
interceptKey(upEvent, assertNotSentToApps)
metaState = metaState and MODIFIER.getOrDefault(key, 0).inv()
@@ -1833,13 +1821,9 @@ class KeyGestureControllerTests {
keyGestureController.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE)
testLooper.dispatchAll()
- val consumed =
- keyGestureController.interceptKeyBeforeDispatching(null, event, 0) == -1L
+ val consumed = keyGestureController.interceptKeyBeforeDispatching(null, event, 0) == -1L
if (assertNotSentToApps) {
- assertTrue(
- "interceptKeyBeforeDispatching should consume all events $event",
- consumed
- )
+ assertTrue("interceptKeyBeforeDispatching should consume all events $event", consumed)
}
if (!consumed) {
keyGestureController.interceptUnhandledKey(event, null)
diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
index 4f4c97bef4c0..1fa985647513 100644
--- a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
@@ -51,31 +51,32 @@ private fun createKeyboard(deviceId: Int): InputDevice =
/**
* Tests for {@link KeyRemapper}.
*
- * Build/Install/Run:
- * atest InputTests:KeyRemapperTests
+ * Build/Install/Run: atest InputTests:KeyRemapperTests
*/
@Presubmit
class KeyRemapperTests {
companion object {
const val DEVICE_ID = 1
- val REMAPPABLE_KEYS = intArrayOf(
- KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT,
- KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT,
- KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT,
- KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT,
- KeyEvent.KEYCODE_CAPS_LOCK
- )
+ val REMAPPABLE_KEYS =
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_CTRL_RIGHT,
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_META_RIGHT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_ALT_RIGHT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_SHIFT_RIGHT,
+ KeyEvent.KEYCODE_CAPS_LOCK,
+ )
}
- @get:Rule
- val rule = MockitoJUnit.rule()!!
+ @get:Rule val rule = MockitoJUnit.rule()!!
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
- @Mock
- private lateinit var native: NativeInputManagerService
+ @Mock private lateinit var native: NativeInputManagerService
private lateinit var mKeyRemapper: KeyRemapper
private lateinit var context: Context
private lateinit var dataStore: PersistentDataStore
@@ -84,24 +85,22 @@ class KeyRemapperTests {
@Before
fun setup() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
- override fun openRead(): InputStream? {
- throw FileNotFoundException()
- }
-
- override fun startWrite(): FileOutputStream? {
- throw IOException()
- }
-
- override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
- })
+ dataStore =
+ PersistentDataStore(
+ object : PersistentDataStore.Injector() {
+ override fun openRead(): InputStream? {
+ throw FileNotFoundException()
+ }
+
+ override fun startWrite(): FileOutputStream? {
+ throw IOException()
+ }
+
+ override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
+ }
+ )
testLooper = TestLooper()
- mKeyRemapper = KeyRemapper(
- context,
- native,
- dataStore,
- testLooper.looper
- )
+ mKeyRemapper = KeyRemapper(context, native, dataStore, testLooper.looper)
val inputManager = InputManager(context)
Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
@@ -131,7 +130,7 @@ class KeyRemapperTests {
assertEquals(
"Remapping should include mapping from $fromKeyCode to $toKeyCode",
toKeyCode,
- remapping.getOrDefault(fromKeyCode, -1)
+ remapping.getOrDefault(fromKeyCode, -1),
)
}
@@ -141,7 +140,7 @@ class KeyRemapperTests {
assertEquals(
"Remapping size should be 0 after clearAllModifierKeyRemappings",
0,
- mKeyRemapper.keyRemapping.size
+ mKeyRemapper.keyRemapping.size,
)
}
}
@@ -159,7 +158,7 @@ class KeyRemapperTests {
assertEquals(
"Remapping should not be done if modifier key remapping is disabled",
0,
- remapping.size
+ remapping.size,
)
}
}
@@ -168,7 +167,8 @@ class KeyRemapperTests {
init {
Settings.Global.putString(
context.contentResolver,
- "settings_new_keyboard_modifier_key", enabled.toString()
+ "settings_new_keyboard_modifier_key",
+ enabled.toString(),
)
}
@@ -176,8 +176,8 @@ class KeyRemapperTests {
Settings.Global.putString(
context.contentResolver,
"settings_new_keyboard_modifier_key",
- ""
+ "",
)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
index 644d5a0679de..cf09b54753b0 100644
--- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -29,8 +29,8 @@ import android.os.SystemProperties
import android.os.UEventObserver
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
-import android.view.InputDevice
import android.util.TypedValue
+import android.view.InputDevice
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
import com.android.dx.mockito.inline.extended.ExtendedMockito
@@ -65,12 +65,7 @@ private fun createKeyboard(deviceId: Int): InputDevice =
.setExternal(true)
.build()
-private fun createLight(lightId: Int, lightType: Int): Light =
- createLight(
- lightId,
- lightType,
- null
- )
+private fun createLight(lightId: Int, lightType: Int): Light = createLight(lightId, lightType, null)
private fun createLight(lightId: Int, lightType: Int, suggestedBrightnessLevels: IntArray?): Light =
Light(
@@ -79,13 +74,13 @@ private fun createLight(lightId: Int, lightType: Int, suggestedBrightnessLevels:
1,
lightType,
Light.LIGHT_CAPABILITY_BRIGHTNESS,
- suggestedBrightnessLevels
+ suggestedBrightnessLevels,
)
+
/**
* Tests for {@link KeyboardBacklightController}.
*
- * Build/Install/Run:
- * atest InputTests:KeyboardBacklightControllerTests
+ * Build/Install/Run: atest InputTests:KeyboardBacklightControllerTests
*/
@Presubmit
class KeyboardBacklightControllerTests {
@@ -100,15 +95,11 @@ class KeyboardBacklightControllerTests {
@get:Rule
val extendedMockitoRule =
ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!!
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
-
- @Mock
- private lateinit var native: NativeInputManagerService
- @Mock
- private lateinit var uEventManager: UEventManager
- @Mock
- private lateinit var resources: Resources
+ @get:Rule val inputManagerRule = MockInputManagerRule()
+
+ @Mock private lateinit var native: NativeInputManagerService
+ @Mock private lateinit var uEventManager: UEventManager
+ @Mock private lateinit var resources: Resources
private lateinit var keyboardBacklightController: KeyboardBacklightController
private lateinit var context: Context
private lateinit var testLooper: TestLooper
@@ -135,9 +126,7 @@ class KeyboardBacklightControllerTests {
lightColorMap.getOrDefault(args[1] as Int, 0)
}
lightColorMap.clear()
- `when`(native.sysfsNodeChanged(any())).then {
- sysfsNodeChanges++
- }
+ `when`(native.sysfsNodeChanged(any())).then { sysfsNodeChanges++ }
}
private fun setupConfig() {
@@ -153,22 +142,29 @@ class KeyboardBacklightControllerTests {
`when`(resources.getInteger(R.integer.config_keyboardBacklightTimeoutMs))
.thenReturn(USER_INACTIVITY_THRESHOLD_MILLIS)
`when`(
- resources.getValue(
- eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
- any(TypedValue::class.java),
- anyBoolean()
+ resources.getValue(
+ eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
+ any(TypedValue::class.java),
+ anyBoolean(),
+ )
)
- ).then {
- val args = it.arguments
- val outValue = args[1] as TypedValue
- outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
- Unit
- }
+ .then {
+ val args = it.arguments
+ val outValue = args[1] as TypedValue
+ outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
+ Unit
+ }
}
private fun setupController() {
- keyboardBacklightController = KeyboardBacklightController(context, native,
- testLooper.looper, FakeAnimatorFactory(), uEventManager)
+ keyboardBacklightController =
+ KeyboardBacklightController(
+ context,
+ native,
+ testLooper.looper,
+ FakeAnimatorFactory(),
+ uEventManager,
+ )
}
@Test
@@ -180,8 +176,11 @@ class KeyboardBacklightControllerTests {
`when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
- assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
- DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL)
+ assertIncrementDecrementForLevels(
+ keyboardWithBacklight,
+ keyboardBacklight,
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL,
+ )
}
@Test
@@ -204,12 +203,8 @@ class KeyboardBacklightControllerTests {
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT)
`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(
- listOf(
- keyboardBacklight,
- keyboardInputLight
- )
- )
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID))
+ .thenReturn(listOf(keyboardBacklight, keyboardInputLight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
incrementKeyboardBacklight(DEVICE_ID)
@@ -239,22 +234,22 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Backlight state device Id should be $DEVICE_ID",
DEVICE_ID,
- lastBacklightState!!.deviceId
+ lastBacklightState!!.deviceId,
)
assertEquals(
"Backlight state brightnessLevel should be 1",
1,
- lastBacklightState!!.brightnessLevel
+ lastBacklightState!!.brightnessLevel,
)
assertEquals(
"Backlight state maxBrightnessLevel should be $maxLevel",
maxLevel,
- lastBacklightState!!.maxBrightnessLevel
+ lastBacklightState!!.maxBrightnessLevel,
)
assertEquals(
"Backlight state isTriggeredByKeyPress should be true",
true,
- lastBacklightState!!.isTriggeredByKeyPress
+ lastBacklightState!!.isTriggeredByKeyPress,
)
// Unregister listener
@@ -278,7 +273,7 @@ class KeyboardBacklightControllerTests {
assertNotEquals(
"Keyboard backlight level should be incremented to a non-zero value",
0,
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
testLooper.moveTimeForward((USER_INACTIVITY_THRESHOLD_MILLIS + 1000).toLong())
@@ -286,7 +281,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Keyboard backlight level should be turned off after inactivity",
0,
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
}
@@ -304,21 +299,21 @@ class KeyboardBacklightControllerTests {
assertNotEquals(
"Keyboard backlight level should be incremented to a non-zero value",
0,
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */)
assertEquals(
"Keyboard backlight level should be turned off after display is turned off",
0,
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */)
assertEquals(
"Keyboard backlight level should be turned on after display is turned on",
currentValue,
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
}
@@ -326,58 +321,70 @@ class KeyboardBacklightControllerTests {
fun testKeyboardBacklightSysfsNodeAdded_AfterInputDeviceAdded() {
setupController()
var counter = sysfsNodeChanges
- keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
- "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::no_backlight\u0000"
- ))
+ keyboardBacklightController.onKeyboardBacklightUEvent(
+ UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::no_backlight\u0000"
+ )
+ )
assertEquals(
"Should not reload sysfs node if UEvent path doesn't contain kbd_backlight",
counter,
- sysfsNodeChanges
+ sysfsNodeChanges,
)
- keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
- "ACTION=add\u0000SUBSYSTEM=power\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
- ))
+ keyboardBacklightController.onKeyboardBacklightUEvent(
+ UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=power\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
+ )
+ )
assertEquals(
"Should not reload sysfs node if UEvent doesn't belong to subsystem LED",
counter,
- sysfsNodeChanges
+ sysfsNodeChanges,
)
- keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
- "ACTION=remove\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
- ))
+ keyboardBacklightController.onKeyboardBacklightUEvent(
+ UEventObserver.UEvent(
+ "ACTION=remove\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
+ )
+ )
assertEquals(
"Should not reload sysfs node if UEvent doesn't have ACTION(add)",
counter,
- sysfsNodeChanges
+ sysfsNodeChanges,
)
- keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
- "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/pqr/abc::kbd_backlight\u0000"
- ))
+ keyboardBacklightController.onKeyboardBacklightUEvent(
+ UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/pqr/abc::kbd_backlight\u0000"
+ )
+ )
assertEquals(
"Should not reload sysfs node if UEvent path doesn't belong to leds/ directory",
counter,
- sysfsNodeChanges
+ sysfsNodeChanges,
)
- keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
- "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
- ))
+ keyboardBacklightController.onKeyboardBacklightUEvent(
+ UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
+ )
+ )
assertEquals(
"Should reload sysfs node if a valid Keyboard backlight LED UEvent occurs",
++counter,
- sysfsNodeChanges
+ sysfsNodeChanges,
)
- keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
- "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc:kbd_backlight:red\u0000"
- ))
+ keyboardBacklightController.onKeyboardBacklightUEvent(
+ UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc:kbd_backlight:red\u0000"
+ )
+ )
assertEquals(
"Should reload sysfs node if a valid Keyboard backlight LED UEvent occurs",
++counter,
- sysfsNodeChanges
+ sysfsNodeChanges,
)
}
@@ -398,12 +405,12 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Should start animation from level 0",
DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[0],
- lastAnimationValues[0]
+ lastAnimationValues[0],
)
assertEquals(
"Should start animation to level 1",
DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1],
- lastAnimationValues[1]
+ lastAnimationValues[1],
)
}
@@ -412,8 +419,8 @@ class KeyboardBacklightControllerTests {
setupController()
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255)
- val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
- suggestedLevels)
+ val keyboardBacklight =
+ createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels)
`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
`when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
@@ -426,14 +433,17 @@ class KeyboardBacklightControllerTests {
setupController()
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) }
- val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
- suggestedLevels)
+ val keyboardBacklight =
+ createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels)
`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
`when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
- assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
- DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL)
+ assertIncrementDecrementForLevels(
+ keyboardWithBacklight,
+ keyboardBacklight,
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL,
+ )
}
@Test
@@ -441,15 +451,18 @@ class KeyboardBacklightControllerTests {
setupController()
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val suggestedLevels = intArrayOf(22, 63, 135, 196)
- val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
- suggestedLevels)
+ val keyboardBacklight =
+ createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels)
`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
`when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
// Framework will add the lowest and maximum levels if not provided via config
- assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
- intArrayOf(0, 22, 63, 135, 196, 255))
+ assertIncrementDecrementForLevels(
+ keyboardWithBacklight,
+ keyboardBacklight,
+ intArrayOf(0, 22, 63, 135, 196, 255),
+ )
}
@Test
@@ -457,15 +470,18 @@ class KeyboardBacklightControllerTests {
setupController()
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000)
- val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
- suggestedLevels)
+ val keyboardBacklight =
+ createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels)
`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
`when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
// Framework will drop out of bound levels in the config
- assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
- intArrayOf(0, 22, 63, 135, 196, 255))
+ assertIncrementDecrementForLevels(
+ keyboardWithBacklight,
+ keyboardBacklight,
+ intArrayOf(0, 22, 63, 135, 196, 255),
+ )
}
@Test
@@ -480,7 +496,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value should be changed to ambient provided value",
Color.argb(1, 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
incrementKeyboardBacklight(DEVICE_ID)
@@ -488,7 +504,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value for level after increment post Ambient change is mismatched",
Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
}
@@ -504,7 +520,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value should be changed to ambient provided value",
Color.argb(254, 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
decrementKeyboardBacklight(DEVICE_ID)
@@ -513,7 +529,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value for level after decrement post Ambient change is mismatched",
Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[numLevels - 2], 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
}
@@ -529,21 +545,21 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value should be changed to the first level",
Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
sendAmbientBacklightValue(100)
assertNotEquals(
"Light value should not change based on ambient changes after manual changes",
Color.argb(100, 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ lightColorMap[LIGHT_ID],
)
}
private fun assertIncrementDecrementForLevels(
device: InputDevice,
light: Light,
- expectedLevels: IntArray
+ expectedLevels: IntArray,
) {
val deviceId = device.id
val lightId = light.id
@@ -552,7 +568,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value for level $level mismatched",
Color.argb(expectedLevels[level], 0, 0, 0),
- lightColorMap[lightId]
+ lightColorMap[lightId],
)
}
@@ -561,7 +577,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value for max level mismatched",
Color.argb(MAX_BRIGHTNESS, 0, 0, 0),
- lightColorMap[lightId]
+ lightColorMap[lightId],
)
for (level in expectedLevels.size - 2 downTo 0) {
@@ -569,7 +585,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value for level $level mismatched",
Color.argb(expectedLevels[level], 0, 0, 0),
- lightColorMap[lightId]
+ lightColorMap[lightId],
)
}
@@ -578,7 +594,7 @@ class KeyboardBacklightControllerTests {
assertEquals(
"Light value for min level mismatched",
Color.argb(0, 0, 0, 0),
- lightColorMap[lightId]
+ lightColorMap[lightId],
)
}
@@ -586,14 +602,15 @@ class KeyboardBacklightControllerTests {
override fun onBrightnessChanged(
deviceId: Int,
state: IKeyboardBacklightState,
- isTriggeredByKeyPress: Boolean
+ isTriggeredByKeyPress: Boolean,
) {
- lastBacklightState = KeyboardBacklightState(
- deviceId,
- state.brightnessLevel,
- state.maxBrightnessLevel,
- isTriggeredByKeyPress
- )
+ lastBacklightState =
+ KeyboardBacklightState(
+ deviceId,
+ state.brightnessLevel,
+ state.maxBrightnessLevel,
+ isTriggeredByKeyPress,
+ )
}
}
@@ -619,7 +636,7 @@ class KeyboardBacklightControllerTests {
val deviceId: Int,
val brightnessLevel: Int,
val maxBrightnessLevel: Int,
- val isTriggeredByKeyPress: Boolean
+ val isTriggeredByKeyPress: Boolean,
)
private inner class FakeAnimatorFactory : KeyboardBacklightController.AnimatorFactory {
diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
index 5da0beb9cc8a..7c3a6a60d3b6 100644
--- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
@@ -49,8 +49,7 @@ import org.mockito.junit.MockitoJUnit
/**
* Tests for custom keyboard glyph map configuration.
*
- * Build/Install/Run:
- * atest InputTests:KeyboardGlyphManagerTests
+ * Build/Install/Run: atest InputTests:KeyboardGlyphManagerTests
*/
@Presubmit
class KeyboardGlyphManagerTests {
@@ -66,15 +65,11 @@ class KeyboardGlyphManagerTests {
const val RECEIVER_NAME = "DummyReceiver"
}
- @get:Rule
- val setFlagsRule = SetFlagsRule()
- @get:Rule
- val mockitoRule = MockitoJUnit.rule()!!
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
+ @get:Rule val mockitoRule = MockitoJUnit.rule()!!
+ @get:Rule val inputManagerRule = MockInputManagerRule()
- @Mock
- private lateinit var packageManager: PackageManager
+ @Mock private lateinit var packageManager: PackageManager
private lateinit var keyboardGlyphManager: KeyboardGlyphManager
private lateinit var context: Context
@@ -99,7 +94,8 @@ class KeyboardGlyphManagerTests {
.thenReturn(inputManager)
keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
- Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID, DEVICE_ID2))
+ Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
+ .thenReturn(intArrayOf(DEVICE_ID, DEVICE_ID2))
Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
val keyboardDevice2 = createKeyboard(DEVICE_ID2, VENDOR_ID2, PRODUCT_ID2, 0, "", "")
@@ -110,19 +106,22 @@ class KeyboardGlyphManagerTests {
Mockito.`when`(context.packageManager).thenReturn(packageManager)
val info = createMockReceiver()
- Mockito.`when`(packageManager.queryBroadcastReceiversAsUser(Mockito.any(), Mockito.anyInt(),
- Mockito.anyInt())).thenReturn(listOf(info))
+ Mockito.`when`(
+ packageManager.queryBroadcastReceiversAsUser(
+ Mockito.any(),
+ Mockito.anyInt(),
+ Mockito.anyInt(),
+ )
+ )
+ .thenReturn(listOf(info))
Mockito.`when`(packageManager.getReceiverInfo(Mockito.any(), Mockito.anyInt()))
.thenReturn(info.activityInfo)
val resources = context.resources
Mockito.`when`(
- packageManager.getResourcesForApplication(
- Mockito.any(
- ApplicationInfo::class.java
- )
+ packageManager.getResourcesForApplication(Mockito.any(ApplicationInfo::class.java))
)
- ).thenReturn(resources)
+ .thenReturn(resources)
}
private fun createMockReceiver(): ResolveInfo {
@@ -134,7 +133,7 @@ class KeyboardGlyphManagerTests {
info.activityInfo.metaData = Bundle()
info.activityInfo.metaData.putInt(
InputManager.META_DATA_KEYBOARD_GLYPH_MAPS,
- R.xml.keyboard_glyph_maps
+ R.xml.keyboard_glyph_maps,
)
info.serviceInfo = ServiceInfo()
info.serviceInfo.packageName = PACKAGE_NAME
@@ -147,15 +146,15 @@ class KeyboardGlyphManagerTests {
fun testGlyphMapsLoaded() {
assertNotNull(
"Glyph map for test keyboard(deviceId=$DEVICE_ID) must exist",
- keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID)
+ keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID),
)
assertNotNull(
"Glyph map for test keyboard(deviceId=$DEVICE_ID2) must exist",
- keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID2)
+ keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID2),
)
assertNull(
"Glyph map for non-existing keyboard must be null",
- keyboardGlyphManager.getKeyGlyphMap(-2)
+ keyboardGlyphManager.getKeyGlyphMap(-2),
)
}
@@ -178,16 +177,15 @@ class KeyboardGlyphManagerTests {
assertEquals(2, hardwareShortcuts.size)
assertEquals(
KeyEvent.KEYCODE_BACK,
- hardwareShortcuts[KeyCombination(KeyEvent.META_FUNCTION_ON, KeyEvent.KEYCODE_1)]
+ hardwareShortcuts[KeyCombination(KeyEvent.META_FUNCTION_ON, KeyEvent.KEYCODE_1)],
)
assertEquals(
KeyEvent.KEYCODE_HOME,
hardwareShortcuts[
KeyCombination(
KeyEvent.META_FUNCTION_ON or KeyEvent.META_META_ON,
- KeyEvent.KEYCODE_2
- )
- ]
+ KeyEvent.KEYCODE_2,
+ )],
)
}
}
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 4440a839caef..c83036ce05ba 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -65,7 +65,7 @@ fun createKeyboard(
productId: Int,
deviceBus: Int,
languageTag: String,
- layoutType: String
+ layoutType: String,
): InputDevice =
InputDevice.Builder()
.setId(deviceId)
@@ -84,8 +84,7 @@ fun createKeyboard(
/**
* Tests for {@link Default UI} and {@link New UI}.
*
- * Build/Install/Run:
- * atest InputTests:KeyboardLayoutManagerTests
+ * Build/Install/Run: atest InputTests:KeyboardLayoutManagerTests
*/
@Presubmit
class KeyboardLayoutManagerTests {
@@ -118,19 +117,15 @@ class KeyboardLayoutManagerTests {
@JvmField
@Rule
- val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
- .mockStatic(FrameworkStatsLog::class.java).build()!!
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).mockStatic(FrameworkStatsLog::class.java).build()!!
- @get:Rule
- val inputManagerRule = MockInputManagerRule()
+ @get:Rule val inputManagerRule = MockInputManagerRule()
- @Mock
- private lateinit var native: NativeInputManagerService
+ @Mock private lateinit var native: NativeInputManagerService
- @Mock
- private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var notificationManager: NotificationManager
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var notificationManager: NotificationManager
private lateinit var keyboardLayoutManager: KeyboardLayoutManager
private lateinit var imeInfo: InputMethodInfo
@@ -149,23 +144,25 @@ class KeyboardLayoutManagerTests {
@Before
fun setup() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
- override fun openRead(): InputStream? {
- throw FileNotFoundException()
- }
-
- override fun startWrite(): FileOutputStream? {
- throw IOException()
- }
-
- override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
- })
+ dataStore =
+ PersistentDataStore(
+ object : PersistentDataStore.Injector() {
+ override fun openRead(): InputStream? {
+ throw FileNotFoundException()
+ }
+
+ override fun startWrite(): FileOutputStream? {
+ throw IOException()
+ }
+
+ override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
+ }
+ )
testLooper = TestLooper()
- keyboardLayoutManager = Mockito.spy(
- KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
- )
+ keyboardLayoutManager =
+ Mockito.spy(KeyboardLayoutManager(context, native, dataStore, testLooper.looper))
Mockito.`when`(context.getSystemService(Mockito.eq(Context.NOTIFICATION_SERVICE)))
- .thenReturn(notificationManager)
+ .thenReturn(notificationManager)
setupInputDevices()
setupBroadcastReceiver()
setupIme()
@@ -176,47 +173,72 @@ class KeyboardLayoutManagerTests {
Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
- keyboardDevice = createKeyboard(DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID,
- DEFAULT_DEVICE_BUS, "", "")
- vendorSpecificKeyboardDevice = createKeyboard(VENDOR_SPECIFIC_DEVICE_ID, 1, 1,
- 1, "", "")
- englishDvorakKeyboardDevice = createKeyboard(ENGLISH_DVORAK_DEVICE_ID, DEFAULT_VENDOR_ID,
- DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak")
- englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID,
- DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty")
- Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
- .thenReturn(intArrayOf(
+ keyboardDevice =
+ createKeyboard(
DEVICE_ID,
- VENDOR_SPECIFIC_DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ "",
+ "",
+ )
+ vendorSpecificKeyboardDevice = createKeyboard(VENDOR_SPECIFIC_DEVICE_ID, 1, 1, 1, "", "")
+ englishDvorakKeyboardDevice =
+ createKeyboard(
ENGLISH_DVORAK_DEVICE_ID,
- ENGLISH_QWERTY_DEVICE_ID
- ))
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ "en",
+ "dvorak",
+ )
+ englishQwertyKeyboardDevice =
+ createKeyboard(
+ ENGLISH_QWERTY_DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ "en",
+ "qwerty",
+ )
+ Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
+ .thenReturn(
+ intArrayOf(
+ DEVICE_ID,
+ VENDOR_SPECIFIC_DEVICE_ID,
+ ENGLISH_DVORAK_DEVICE_ID,
+ ENGLISH_QWERTY_DEVICE_ID,
+ )
+ )
Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
Mockito.`when`(inputManagerRule.mock.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
.thenReturn(vendorSpecificKeyboardDevice)
Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
.thenReturn(englishDvorakKeyboardDevice)
Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
- .thenReturn(englishQwertyKeyboardDevice)
+ .thenReturn(englishQwertyKeyboardDevice)
}
private fun setupBroadcastReceiver() {
Mockito.`when`(context.packageManager).thenReturn(packageManager)
val info = createMockReceiver()
- Mockito.`when`(packageManager.queryBroadcastReceiversAsUser(Mockito.any(), Mockito.anyInt(),
- Mockito.anyInt())).thenReturn(listOf(info))
+ Mockito.`when`(
+ packageManager.queryBroadcastReceiversAsUser(
+ Mockito.any(),
+ Mockito.anyInt(),
+ Mockito.anyInt(),
+ )
+ )
+ .thenReturn(listOf(info))
Mockito.`when`(packageManager.getReceiverInfo(Mockito.any(), Mockito.anyInt()))
.thenReturn(info.activityInfo)
val resources = context.resources
Mockito.`when`(
- packageManager.getResourcesForApplication(
- Mockito.any(
- ApplicationInfo::class.java
- )
+ packageManager.getResourcesForApplication(Mockito.any(ApplicationInfo::class.java))
)
- ).thenReturn(resources)
+ .thenReturn(resources)
}
private fun setupIme() {
@@ -229,22 +251,21 @@ class KeyboardLayoutManagerTests {
assertNotEquals(
"Keyboard layout API should not return empty array",
0,
- keyboardLayouts.size
+ keyboardLayouts.size,
)
assertTrue(
"Keyboard layout API should provide English(US) layout",
- hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR),
)
}
@Test
fun testGetKeyboardLayout() {
- val keyboardLayout =
- keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
- assertEquals("getKeyboardLayout API should return correct Layout from " +
- "available layouts",
+ val keyboardLayout = keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
+ assertEquals(
+ "getKeyboardLayout API should return correct Layout from " + "available layouts",
ENGLISH_US_LAYOUT_DESCRIPTOR,
- keyboardLayout!!.descriptor
+ keyboardLayout!!.descriptor,
)
}
@@ -253,32 +274,44 @@ class KeyboardLayoutManagerTests {
val imeSubtype = createImeSubtype()
keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_UK_LAYOUT_DESCRIPTOR
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
)
var result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
)
assertEquals(
"getKeyboardLayoutForInputDevice API should return the set layout",
ENGLISH_UK_LAYOUT_DESCRIPTOR,
- result.layoutDescriptor
+ result.layoutDescriptor,
)
// This should replace previously set layout
keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
)
assertEquals(
"getKeyboardLayoutForInputDevice API should return the last set layout",
ENGLISH_US_LAYOUT_DESCRIPTOR,
- result.layoutDescriptor
+ result.layoutDescriptor,
)
}
@@ -288,32 +321,42 @@ class KeyboardLayoutManagerTests {
keyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice(
keyboardDevice.identifier,
- ENGLISH_UK_LAYOUT_DESCRIPTOR
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
)
var result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
)
assertEquals(LAYOUT_SELECTION_CRITERIA_DEVICE, result.selectionCriteria)
assertEquals(
"getKeyboardLayoutForInputDevice API should return the set layout",
ENGLISH_UK_LAYOUT_DESCRIPTOR,
- result.layoutDescriptor
+ result.layoutDescriptor,
)
// This should replace the overriding layout set above
keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_US_LAYOUT_DESCRIPTOR
- )
- result = keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
+ result =
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
+ )
assertEquals(LAYOUT_SELECTION_CRITERIA_USER, result.selectionCriteria)
assertEquals(
"getKeyboardLayoutForInputDevice API should return the user set layout",
ENGLISH_US_LAYOUT_DESCRIPTOR,
- result.layoutDescriptor
+ result.layoutDescriptor,
)
}
@@ -322,54 +365,67 @@ class KeyboardLayoutManagerTests {
// Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts
var keyboardLayouts =
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("hi-Latn")
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTag("hi-Latn"),
)
assertNotEquals(
"getKeyboardLayoutListForInputDevice API should return the list of " +
- "supported layouts with matching script code",
+ "supported layouts with matching script code",
0,
- keyboardLayouts.size
+ keyboardLayouts.size,
)
- assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ assertTrue(
+ "getKeyboardLayoutListForInputDevice API should return a list " +
"containing English(US) layout for hi-Latn",
- containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR),
)
- assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ assertTrue(
+ "getKeyboardLayoutListForInputDevice API should return a list " +
"containing English(No script code) layout for hi-Latn",
containsLayout(
keyboardLayouts,
- createLayoutDescriptor("keyboard_layout_english_without_script_code")
- )
+ createLayoutDescriptor("keyboard_layout_english_without_script_code"),
+ ),
)
// Check Layouts for "hi" which by default uses 'Deva' script.
keyboardLayouts =
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("hi")
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTag("hi"),
)
- assertEquals("getKeyboardLayoutListForInputDevice API should return empty " +
+ assertEquals(
+ "getKeyboardLayoutListForInputDevice API should return empty " +
"list if no supported layouts available",
0,
- keyboardLayouts.size
+ keyboardLayouts.size,
)
// If user manually selected some layout, always provide it in the layout list
val imeSubtype = createImeSubtypeForLanguageTag("hi")
keyboardLayoutManager.setKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
keyboardLayouts =
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- imeSubtype
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
)
- assertEquals("getKeyboardLayoutListForInputDevice API should return user " +
+ assertEquals(
+ "getKeyboardLayoutListForInputDevice API should return user " +
"selected layout even if the script is incompatible with IME",
- 1,
- keyboardLayouts.size
+ 1,
+ keyboardLayouts.size,
)
// Special case Japanese: UScript ignores provided script code for certain language tags
@@ -377,63 +433,71 @@ class KeyboardLayoutManagerTests {
// script from language tags and match those.
keyboardLayouts =
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("ja-Latn-JP")
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTag("ja-Latn-JP"),
)
assertNotEquals(
"getKeyboardLayoutListForInputDevice API should return the list of " +
- "supported layouts with matching script code for ja-Latn-JP",
+ "supported layouts with matching script code for ja-Latn-JP",
0,
- keyboardLayouts.size
+ keyboardLayouts.size,
)
- assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ assertTrue(
+ "getKeyboardLayoutListForInputDevice API should return a list " +
"containing English(US) layout for ja-Latn-JP",
- containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR),
)
- assertTrue("getKeyboardLayoutListForInputDevice API should return a list " +
+ assertTrue(
+ "getKeyboardLayoutListForInputDevice API should return a list " +
"containing English(No script code) layout for ja-Latn-JP",
containsLayout(
keyboardLayouts,
- createLayoutDescriptor("keyboard_layout_english_without_script_code")
- )
+ createLayoutDescriptor("keyboard_layout_english_without_script_code"),
+ ),
)
// If script code not explicitly provided for Japanese should rely on Uscript to find
// derived script code and hence no suitable layout will be found.
keyboardLayouts =
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("ja-JP")
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTag("ja-JP"),
)
assertEquals(
"getKeyboardLayoutListForInputDevice API should return empty list of " +
- "supported layouts with matching script code for ja-JP",
+ "supported layouts with matching script code for ja-JP",
0,
- keyboardLayouts.size
+ keyboardLayouts.size,
)
// If IME doesn't have a corresponding language tag, then should show all available
// layouts no matter the script code.
keyboardLayouts =
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo, null
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ null,
)
assertNotEquals(
"getKeyboardLayoutListForInputDevice API should return all layouts if" +
"language tag or subtype not provided",
0,
- keyboardLayouts.size
+ keyboardLayouts.size,
)
- assertTrue("getKeyboardLayoutListForInputDevice API should contain Latin " +
- "layouts if language tag or subtype not provided",
- containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ assertTrue(
+ "getKeyboardLayoutListForInputDevice API should contain Latin " +
+ "layouts if language tag or subtype not provided",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR),
)
- assertTrue("getKeyboardLayoutListForInputDevice API should contain Cyrillic " +
- "layouts if language tag or subtype not provided",
- containsLayout(
- keyboardLayouts,
- createLayoutDescriptor("keyboard_layout_russian")
- )
+ assertTrue(
+ "getKeyboardLayoutListForInputDevice API should contain Cyrillic " +
+ "layouts if language tag or subtype not provided",
+ containsLayout(keyboardLayouts, createLayoutDescriptor("keyboard_layout_russian")),
)
}
@@ -442,46 +506,50 @@ class KeyboardLayoutManagerTests {
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("en-US"),
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("en-GB"),
- ENGLISH_UK_LAYOUT_DESCRIPTOR
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("de"),
- GERMAN_LAYOUT_DESCRIPTOR
+ GERMAN_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("fr-FR"),
- createLayoutDescriptor("keyboard_layout_french")
+ createLayoutDescriptor("keyboard_layout_french"),
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("ru"),
- createLayoutDescriptor("keyboard_layout_russian")
+ createLayoutDescriptor("keyboard_layout_russian"),
)
assertEquals(
"getDefaultKeyboardLayoutForInputDevice should return " +
- "KeyboardLayoutSelectionResult.FAILED when no layout available",
+ "KeyboardLayoutSelectionResult.FAILED when no layout available",
KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("it")
- )
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTag("it"),
+ ),
)
assertEquals(
"getDefaultKeyboardLayoutForInputDevice should return " +
- "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
- "available",
+ "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
+ "available",
KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTag("en-Deva")
- )
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTag("en-Deva"),
+ ),
)
}
@@ -490,72 +558,75 @@ class KeyboardLayoutManagerTests {
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("en-US", "qwerty"),
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("en-US", "dvorak"),
- createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak"),
)
// Try to match layout type even if country doesn't match
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("en-GB", "dvorak"),
- createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak"),
)
// Choose layout based on layout type priority, if layout type is not provided by IME
// (Qwerty > Dvorak > Extended)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("en-US", ""),
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("en-GB", "qwerty"),
- ENGLISH_UK_LAYOUT_DESCRIPTOR
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"),
- GERMAN_LAYOUT_DESCRIPTOR
+ GERMAN_LAYOUT_DESCRIPTOR,
)
// Wrong layout type should match with language if provided layout type not available
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"),
- GERMAN_LAYOUT_DESCRIPTOR
+ GERMAN_LAYOUT_DESCRIPTOR,
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("fr-FR", "azerty"),
- createLayoutDescriptor("keyboard_layout_french")
+ createLayoutDescriptor("keyboard_layout_french"),
)
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("ru", "qwerty"),
- createLayoutDescriptor("keyboard_layout_russian_qwerty")
+ createLayoutDescriptor("keyboard_layout_russian_qwerty"),
)
// If layout type is empty then prioritize KCM with empty layout type
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
- createLayoutDescriptor("keyboard_layout_russian")
+ createLayoutDescriptor("keyboard_layout_russian"),
)
- assertEquals("getDefaultKeyboardLayoutForInputDevice should return " +
+ assertEquals(
+ "getDefaultKeyboardLayoutForInputDevice should return " +
"KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
"available",
KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- keyboardDevice.identifier, USER_ID, imeInfo,
- createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
- )
+ keyboardDevice.identifier,
+ USER_ID,
+ imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", ""),
+ ),
)
// If prefer layout with empty country over mismatched country
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("en-AU", "qwerty"),
- ENGLISH_US_LAYOUT_DESCRIPTOR
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
)
}
@@ -567,7 +638,7 @@ class KeyboardLayoutManagerTests {
assertCorrectLayout(
englishDvorakKeyboardDevice,
frenchSubtype,
- createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak"),
)
// Back to back changing HW keyboards with same product and vendor ID but different
@@ -575,7 +646,7 @@ class KeyboardLayoutManagerTests {
assertCorrectLayout(
englishQwertyKeyboardDevice,
frenchSubtype,
- createLayoutDescriptor("keyboard_layout_english_us")
+ createLayoutDescriptor("keyboard_layout_english_us"),
)
// Fallback to IME information if the HW provided layout script is incompatible with the
@@ -583,62 +654,72 @@ class KeyboardLayoutManagerTests {
assertCorrectLayout(
englishDvorakKeyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
- createLayoutDescriptor("keyboard_layout_russian")
+ createLayoutDescriptor("keyboard_layout_russian"),
)
}
@Test
fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() {
- val imeInfos = listOf(
- KeyboardLayoutManager.ImeInfo(0, imeInfo,
- createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+ val imeInfos =
+ listOf(
+ KeyboardLayoutManager.ImeInfo(
+ 0,
+ imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"),
+ )
+ )
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
ExtendedMockito.verify {
FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.eq(keyboardDevice.vendorId),
- ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(
- createByteArray(
- KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT,
- GERMAN_LAYOUT_NAME,
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
- "de-Latn",
- LAYOUT_TYPE_QWERTZ
- ),
- ),
- ArgumentMatchers.eq(keyboardDevice.deviceBus),
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(
+ createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ GERMAN_LAYOUT_NAME,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ,
+ )
+ ),
+ ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
}
@Test
fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() {
- val imeInfos = listOf(
- KeyboardLayoutManager.ImeInfo(0, imeInfo,
- createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+ val imeInfos =
+ listOf(
+ KeyboardLayoutManager.ImeInfo(
+ 0,
+ imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"),
+ )
+ )
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
ExtendedMockito.verify {
FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
- ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
- ArgumentMatchers.eq(
- createByteArray(
- "en",
- LAYOUT_TYPE_QWERTY,
- ENGLISH_US_LAYOUT_NAME,
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
- "de-Latn",
- LAYOUT_TYPE_QWERTZ
- )
- ),
- ArgumentMatchers.eq(keyboardDevice.deviceBus),
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
+ ArgumentMatchers.eq(
+ createByteArray(
+ "en",
+ LAYOUT_TYPE_QWERTY,
+ ENGLISH_US_LAYOUT_NAME,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ,
+ )
+ ),
+ ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
}
@@ -650,21 +731,21 @@ class KeyboardLayoutManagerTests {
keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
ExtendedMockito.verify {
FrameworkStatsLog.write(
- ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
- ArgumentMatchers.anyBoolean(),
- ArgumentMatchers.eq(keyboardDevice.vendorId),
- ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(
- createByteArray(
- KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT,
- "Default",
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT,
- KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT
- ),
- ),
- ArgumentMatchers.eq(keyboardDevice.deviceBus),
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(
+ createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ "Default",
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ )
+ ),
+ ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
}
@@ -674,82 +755,86 @@ class KeyboardLayoutManagerTests {
val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
- ExtendedMockito.verify({
- FrameworkStatsLog.write(
+ ExtendedMockito.verify(
+ {
+ FrameworkStatsLog.write(
ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.anyInt(),
ArgumentMatchers.anyInt(),
ArgumentMatchers.any(ByteArray::class.java),
ArgumentMatchers.anyInt(),
- )
- }, Mockito.times(0))
+ )
+ },
+ Mockito.times(0),
+ )
}
@Test
fun testNotificationShown_onInputDeviceChanged() {
val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
- Mockito.doReturn(false).`when`(keyboardLayoutManager).isVirtualDevice(
- ArgumentMatchers.eq(keyboardDevice.id)
- )
+ Mockito.doReturn(false)
+ .`when`(keyboardLayoutManager)
+ .isVirtualDevice(ArgumentMatchers.eq(keyboardDevice.id))
keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
- ExtendedMockito.verify(
- notificationManager,
- Mockito.times(1)
- ).notifyAsUser(
- ArgumentMatchers.isNull(),
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.any(),
- ArgumentMatchers.any()
- )
+ ExtendedMockito.verify(notificationManager, Mockito.times(1))
+ .notifyAsUser(
+ ArgumentMatchers.isNull(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(),
+ ArgumentMatchers.any(),
+ )
}
@Test
fun testNotificationNotShown_onInputDeviceChanged_forVirtualDevice() {
val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
- Mockito.doReturn(true).`when`(keyboardLayoutManager).isVirtualDevice(
- ArgumentMatchers.eq(keyboardDevice.id)
- )
+ Mockito.doReturn(true)
+ .`when`(keyboardLayoutManager)
+ .isVirtualDevice(ArgumentMatchers.eq(keyboardDevice.id))
keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
- ExtendedMockito.verify(
- notificationManager,
- Mockito.never()
- ).notifyAsUser(
- ArgumentMatchers.isNull(),
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.any(),
- ArgumentMatchers.any()
- )
+ ExtendedMockito.verify(notificationManager, Mockito.never())
+ .notifyAsUser(
+ ArgumentMatchers.isNull(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(),
+ ArgumentMatchers.any(),
+ )
}
private fun assertCorrectLayout(
device: InputDevice,
imeSubtype: InputMethodSubtype,
- expectedLayout: String
+ expectedLayout: String,
) {
- val result = keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- device.identifier, USER_ID, imeInfo, imeSubtype
- )
+ val result =
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ device.identifier,
+ USER_ID,
+ imeInfo,
+ imeSubtype,
+ )
assertEquals(
"getDefaultKeyboardLayoutForInputDevice should return $expectedLayout",
expectedLayout,
- result.layoutDescriptor
+ result.layoutDescriptor,
)
}
private fun createImeSubtype(): InputMethodSubtype =
- createImeSubtypeForLanguageTagAndLayoutType(null, null)
+ createImeSubtypeForLanguageTagAndLayoutType(null, null)
private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype =
- createImeSubtypeForLanguageTagAndLayoutType(languageTag, null)
+ createImeSubtypeForLanguageTagAndLayoutType(languageTag, null)
private fun createImeSubtypeForLanguageTagAndLayoutType(
- languageTag: String?,
- layoutType: String?
+ languageTag: String?,
+ layoutType: String?,
): InputMethodSubtype {
- val builder = InputMethodSubtype.InputMethodSubtypeBuilder()
+ val builder =
+ InputMethodSubtype.InputMethodSubtypeBuilder()
.setSubtypeId(nextImeSubtypeId++)
.setIsAuxiliary(false)
.setSubtypeMode("keyboard")
@@ -762,39 +847,39 @@ class KeyboardLayoutManagerTests {
}
private fun createByteArray(
- expectedLanguageTag: String,
- expectedLayoutType: Int,
- expectedLayoutName: String,
- expectedCriteria: Int,
- expectedImeLanguageTag: String,
- expectedImeLayoutType: Int
+ expectedLanguageTag: String,
+ expectedLayoutType: Int,
+ expectedLayoutName: String,
+ expectedCriteria: Int,
+ expectedImeLanguageTag: String,
+ expectedImeLayoutType: Int,
): ByteArray {
val proto = ProtoOutputStream()
- val keyboardLayoutConfigToken = proto.start(
- KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
+ val keyboardLayoutConfigToken =
+ proto.start(KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
proto.write(
- KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
- expectedLanguageTag
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
+ expectedLanguageTag,
)
proto.write(
- KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
- expectedLayoutType
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
+ expectedLayoutType,
)
proto.write(
- KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
- expectedLayoutName
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
+ expectedLayoutName,
)
proto.write(
- KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
- expectedCriteria
+ KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
+ expectedCriteria,
)
proto.write(
- KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG,
- expectedImeLanguageTag
+ KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG,
+ expectedImeLanguageTag,
)
proto.write(
- KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
- expectedImeLayoutType
+ KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
+ expectedImeLayoutType,
)
proto.end(keyboardLayoutConfigToken)
return proto.bytes
@@ -830,7 +915,7 @@ class KeyboardLayoutManagerTests {
info.activityInfo.metaData = Bundle()
info.activityInfo.metaData.putInt(
InputManager.META_DATA_KEYBOARD_LAYOUTS,
- R.xml.keyboard_layouts
+ R.xml.keyboard_layouts,
)
info.serviceInfo = ServiceInfo()
info.serviceInfo.packageName = PACKAGE_NAME
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
index 0615941eda09..5251f3d3f1d3 100644
--- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -33,7 +33,7 @@ private fun createKeyboard(
productId: Int,
deviceBus: Int,
languageTag: String?,
- layoutType: String?
+ layoutType: String?,
): InputDevice =
InputDevice.Builder()
.setId(deviceId)
@@ -52,16 +52,17 @@ private fun createKeyboard(
private fun createImeSubtype(
imeSubtypeId: Int,
languageTag: ULocale?,
- layoutType: String
+ layoutType: String,
): InputMethodSubtype =
- InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(imeSubtypeId)
- .setPhysicalKeyboardHint(languageTag, layoutType).build()
+ InputMethodSubtype.InputMethodSubtypeBuilder()
+ .setSubtypeId(imeSubtypeId)
+ .setPhysicalKeyboardHint(languageTag, layoutType)
+ .build()
/**
* Tests for {@link KeyboardMetricsCollector}.
*
- * Build/Install/Run:
- * atest InputTests:KeyboardMetricsCollectorTests
+ * Build/Install/Run: atest InputTests:KeyboardMetricsCollectorTests
*/
@Presubmit
class KeyboardMetricsCollectorTests {
@@ -77,15 +78,16 @@ class KeyboardMetricsCollectorTests {
fun testCreateKeyboardConfigurationEvent_throwsExceptionWithoutAnyLayoutConfiguration() {
assertThrows(IllegalStateException::class.java) {
KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
- createKeyboard(
- DEVICE_ID,
- DEFAULT_VENDOR_ID,
- DEFAULT_PRODUCT_ID,
- DEFAULT_DEVICE_BUS,
- null,
- null
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ null,
+ null,
+ )
)
- ).build()
+ .build()
}
}
@@ -93,66 +95,78 @@ class KeyboardMetricsCollectorTests {
fun testCreateKeyboardConfigurationEvent_throwsExceptionWithInvalidLayoutSelectionCriteria() {
assertThrows(IllegalStateException::class.java) {
KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
- createKeyboard(
- DEVICE_ID,
- DEFAULT_VENDOR_ID,
- DEFAULT_PRODUCT_ID,
- DEFAULT_DEVICE_BUS,
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ null,
+ null,
+ )
+ )
+ .addLayoutSelection(
+ createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
null,
- null
+ 123,
)
- ).addLayoutSelection(createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
- null, 123).build()
+ .build()
}
}
@Test
fun testCreateKeyboardConfigurationEvent_withMultipleConfigurations() {
- val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
- createKeyboard(
- DEVICE_ID,
- DEFAULT_VENDOR_ID,
- DEFAULT_PRODUCT_ID,
- DEFAULT_DEVICE_BUS,
- "de-CH",
- "qwertz"
+ val builder =
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ "de-CH",
+ "qwertz",
+ )
)
- )
- val event = builder.addLayoutSelection(
- createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
- "English(US)(Qwerty)",
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
- ).addLayoutSelection(
- createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
- null, // Default layout type
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER
- ).addLayoutSelection(
- createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
- "German",
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
- ).setIsFirstTimeConfiguration(true).build()
+ val event =
+ builder
+ .addLayoutSelection(
+ createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
+ "English(US)(Qwerty)",
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ )
+ .addLayoutSelection(
+ createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
+ null, // Default layout type
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER,
+ )
+ .addLayoutSelection(
+ createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
+ "German",
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ )
+ .setIsFirstTimeConfiguration(true)
+ .build()
assertEquals(
"KeyboardConfigurationEvent should pick vendor ID from provided InputDevice",
DEFAULT_VENDOR_ID,
- event.vendorId
+ event.vendorId,
)
assertEquals(
"KeyboardConfigurationEvent should pick product ID from provided InputDevice",
DEFAULT_PRODUCT_ID,
- event.productId
+ event.productId,
)
assertEquals(
- "KeyboardConfigurationEvent should pick device bus from provided InputDevice",
- DEFAULT_DEVICE_BUS,
- event.deviceBus
+ "KeyboardConfigurationEvent should pick device bus from provided InputDevice",
+ DEFAULT_DEVICE_BUS,
+ event.deviceBus,
)
assertTrue(event.isFirstConfiguration)
assertEquals(
"KeyboardConfigurationEvent should contain 3 configurations provided",
3,
- event.layoutConfigurations.size
+ event.layoutConfigurations.size,
)
assertExpectedLayoutConfiguration(
event.layoutConfigurations[0],
@@ -185,21 +199,25 @@ class KeyboardMetricsCollectorTests {
@Test
fun testCreateKeyboardConfigurationEvent_withDefaultLanguageTag() {
- val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
- createKeyboard(
- DEVICE_ID,
- DEFAULT_VENDOR_ID,
- DEFAULT_PRODUCT_ID,
- DEFAULT_DEVICE_BUS,
- "und", // Undefined language tag
- "azerty"
+ val builder =
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ DEFAULT_DEVICE_BUS,
+ "und", // Undefined language tag
+ "azerty",
+ )
)
- )
- val event = builder.addLayoutSelection(
- createImeSubtype(4, null, "qwerty"), // Default language tag
- "German",
- KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
- ).build()
+ val event =
+ builder
+ .addLayoutSelection(
+ createImeSubtype(4, null, "qwerty"), // Default language tag
+ "German",
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ )
+ .build()
assertExpectedLayoutConfiguration(
event.layoutConfigurations[0],
@@ -219,7 +237,7 @@ class KeyboardMetricsCollectorTests {
expectedSelectedLayout: String,
expectedLayoutSelectionCriteria: Int,
expectedImeLanguageTag: String,
- expectedImeLayoutType: Int
+ expectedImeLayoutType: Int,
) {
assertEquals(expectedKeyboardLanguageTag, configuration.keyboardLanguageTag)
assertEquals(expectedKeyboardLayoutType, configuration.keyboardLayoutType)
diff --git a/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt
index 47e7ac720a08..d9ae7f339ed9 100644
--- a/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt
+++ b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt
@@ -34,19 +34,14 @@ import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-/**
- * Tests for {@link PointerIconCache}.
- */
+/** Tests for {@link PointerIconCache}. */
@Presubmit
class PointerIconCacheTest {
- @get:Rule
- val rule = MockitoJUnit.rule()!!
+ @get:Rule val rule = MockitoJUnit.rule()!!
- @Mock
- private lateinit var native: NativeInputManagerService
- @Mock
- private lateinit var defaultDisplay: Display
+ @Mock private lateinit var native: NativeInputManagerService
+ @Mock private lateinit var defaultDisplay: Display
private lateinit var context: Context
private lateinit var testLooper: TestLooper
@@ -56,9 +51,10 @@ class PointerIconCacheTest {
fun setup() {
whenever(defaultDisplay.displayId).thenReturn(Display.DEFAULT_DISPLAY)
- context = object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) {
- override fun getDisplay() = defaultDisplay
- }
+ context =
+ object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) {
+ override fun getDisplay() = defaultDisplay
+ }
testLooper = TestLooper()
cache = PointerIconCache(context, native, Handler(testLooper.looper))
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index 015e188fc98e..faa68bd484f6 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -17,10 +17,9 @@
package com.android.test.input
import android.view.InputDevice.SOURCE_MOUSE
-import android.view.InputDevice.SOURCE_TOUCHSCREEN
import android.view.InputDevice.SOURCE_STYLUS
import android.view.InputDevice.SOURCE_TOUCHPAD
-
+import android.view.InputDevice.SOURCE_TOUCHSCREEN
import android.view.InputEventAssigner
import android.view.KeyEvent
import android.view.MotionEvent
@@ -28,13 +27,13 @@ import org.junit.Assert.assertEquals
import org.junit.Test
sealed class StreamEvent
+
private data object Vsync : StreamEvent()
+
data class MotionEventData(val action: Int, val source: Int, val id: Int, val expectedId: Int) :
StreamEvent()
-/**
- * Create a MotionEvent with the provided action, eventTime, and source
- */
+/** Create a MotionEvent with the provided action, eventTime, and source */
fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent {
val downTime: Long = 10
val x = 1f
@@ -47,8 +46,22 @@ fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent {
val deviceId = 1
val edgeFlags = 0
val displayId = 0
- return MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
+ return MotionEvent.obtain(
+ downTime,
+ eventTime,
+ action,
+ x,
+ y,
+ pressure,
+ size,
+ metaState,
+ xPrecision,
+ yPrecision,
+ deviceId,
+ edgeFlags,
+ source,
+ displayId,
+ )
}
private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
@@ -58,11 +71,10 @@ private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
}
/**
- * Check that the correct eventIds are assigned in a stream. The stream consists of motion
- * events or vsync (processed frame)
- * Each streamEvent should have unique ids when writing tests
- * The test passes even if two events get assigned the same eventId, since the mapping is
- * streamEventId -> motionEventId and streamEvents have unique ids
+ * Check that the correct eventIds are assigned in a stream. The stream consists of motion events or
+ * vsync (processed frame) Each streamEvent should have unique ids when writing tests The test
+ * passes even if two events get assigned the same eventId, since the mapping is streamEventId ->
+ * motionEventId and streamEvents have unique ids
*/
private fun checkEventStream(vararg streamEvents: StreamEvent) {
val assigner = InputEventAssigner()
@@ -90,9 +102,7 @@ class InputEventAssignerTest {
private const val TAG = "InputEventAssignerTest"
}
- /**
- * A single event should be assigned to the next available frame.
- */
+ /** A single event should be assigned to the next available frame. */
@Test
fun testTouchMove() {
checkEventStream(
@@ -145,7 +155,7 @@ class InputEventAssignerTest {
MotionEventData(MotionEvent.ACTION_DOWN, source, id = 1, expectedId = 1),
MotionEventData(MotionEvent.ACTION_MOVE, source, id = 2, expectedId = 1),
Vsync,
- MotionEventData(MotionEvent.ACTION_MOVE, source, id = 4, expectedId = 4)
+ MotionEventData(MotionEvent.ACTION_MOVE, source, id = 4, expectedId = 4),
)
}
@@ -169,57 +179,47 @@ class InputEventAssignerTest {
testDownAndMove(SOURCE_TOUCHPAD)
}
- /**
- * After an up event, motion events should be assigned their own event id
- */
+ /** After an up event, motion events should be assigned their own event id */
@Test
fun testMouseDownUpAndScroll() {
checkEventStream(
MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1),
MotionEventData(MotionEvent.ACTION_UP, SOURCE_MOUSE, id = 2, expectedId = 2),
- MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3)
+ MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3),
)
}
- /**
- * After an up event, motion events should be assigned their own event id
- */
+ /** After an up event, motion events should be assigned their own event id */
@Test
fun testStylusDownUpAndHover() {
checkEventStream(
MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1),
MotionEventData(MotionEvent.ACTION_UP, SOURCE_STYLUS, id = 2, expectedId = 2),
- MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3)
+ MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3),
)
}
- /**
- * After a cancel event, motion events should be assigned their own event id
- */
+ /** After a cancel event, motion events should be assigned their own event id */
@Test
fun testMouseDownCancelAndScroll() {
checkEventStream(
MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1),
MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_MOUSE, id = 2, expectedId = 2),
- MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3)
+ MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3),
)
}
- /**
- * After a cancel event, motion events should be assigned their own event id
- */
+ /** After a cancel event, motion events should be assigned their own event id */
@Test
fun testStylusDownCancelAndHover() {
checkEventStream(
MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1),
MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_STYLUS, id = 2, expectedId = 2),
- MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3)
+ MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3),
)
}
- /**
- * KeyEvents are processed immediately, so the latest event should be returned.
- */
+ /** KeyEvents are processed immediately, so the latest event should be returned. */
@Test
fun testKeyEvent() {
val assigner = InputEventAssigner()
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
index 075cf0cc5a45..acd5a7d3f9e2 100644
--- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -22,8 +22,8 @@ import android.view.InputChannel
import android.view.InputEvent
import android.view.InputEventReceiver
import android.view.KeyEvent
-import org.junit.Assert.assertEquals
import org.junit.After
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -42,12 +42,17 @@ private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
}
private fun getTestKeyEvent(): KeyEvent {
- return KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_A, 0 /*repeat*/)
+ return KeyEvent(
+ 1 /*downTime*/,
+ 1 /*eventTime*/,
+ KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A,
+ 0, /*repeat*/
+ )
}
private class CrashingInputEventReceiver(channel: InputChannel, looper: Looper) :
- InputEventReceiver(channel, looper) {
+ InputEventReceiver(channel, looper) {
override fun onInputEvent(event: InputEvent) {
try {
throw IllegalArgumentException("This receiver crashes when it receives input event")
@@ -61,6 +66,7 @@ class InputEventSenderAndReceiverTest {
companion object {
private const val TAG = "InputEventSenderAndReceiverTest"
}
+
private val mHandlerThread = HandlerThread("Process input events")
private lateinit var mReceiver: SpyInputEventReceiver
private lateinit var mSender: SpyInputEventSender
@@ -98,8 +104,8 @@ class InputEventSenderAndReceiverTest {
// The timeline case is slightly unusual because it goes from InputConsumer to InputPublisher.
@Test
fun testSendAndReceiveTimeline() {
- val sent = SpyInputEventSender.Timeline(
- inputEventId = 1, gpuCompletedTime = 2, presentTime = 3)
+ val sent =
+ SpyInputEventSender.Timeline(inputEventId = 1, gpuCompletedTime = 2, presentTime = 3)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
val received = mSender.getTimeline()
assertEquals(sent, received)
@@ -110,8 +116,8 @@ class InputEventSenderAndReceiverTest {
// event processing.
@Test
fun testSendAndReceiveInvalidTimeline() {
- val sent = SpyInputEventSender.Timeline(
- inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
+ val sent =
+ SpyInputEventSender.Timeline(inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
mSender.assertNoEvents()
// Sender will no longer receive callbacks for this fd, even if receiver sends a valid
@@ -123,9 +129,8 @@ class InputEventSenderAndReceiverTest {
/**
* If a receiver throws an exception during 'onInputEvent' execution, the 'finally' block still
* completes, and therefore, finishInputEvent is called. Make sure that there's no crash in the
- * native layer in these circumstances.
- * In this test, we are reusing the 'mHandlerThread', but we are creating new sender and
- * receiver.
+ * native layer in these circumstances. In this test, we are reusing the 'mHandlerThread', but
+ * we are creating new sender and receiver.
*/
@Test
fun testCrashingReceiverDoesNotCrash() {
diff --git a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
index 860d9f680c4c..4ca08d839c54 100644
--- a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
+++ b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
@@ -18,12 +18,9 @@ package com.android.test.input
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
-
import android.view.KeyCharacterMap
import android.view.KeyEvent
-
import com.android.hardware.input.Flags
-
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Rule
@@ -32,13 +29,10 @@ import org.junit.Test
/**
* Tests for {@link KeyCharacterMap}.
*
- * <p>Build/Install/Run:
- * atest KeyCharacterMapTest
- *
+ * <p>Build/Install/Run: atest KeyCharacterMapTest
*/
class KeyCharacterMapTest {
- @get:Rule
- val setFlagsRule = SetFlagsRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
@Test
@EnableFlags(Flags.FLAG_REMOVE_FALLBACK_MODIFIERS)
@@ -47,28 +41,33 @@ class KeyCharacterMapTest {
val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
// One modifier fallback.
- val oneModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
- KeyEvent.META_CTRL_ON)
+ val oneModifierFallback =
+ keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON)
assertEquals(KeyEvent.KEYCODE_LANGUAGE_SWITCH, oneModifierFallback.keyCode)
assertEquals(0, oneModifierFallback.metaState)
// Multiple modifier fallback.
- val twoModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+ val twoModifierFallback =
+ keyCharacterMap.getFallbackAction(
+ KeyEvent.KEYCODE_DEL,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ )
assertEquals(KeyEvent.KEYCODE_BACK, twoModifierFallback.keyCode)
assertEquals(0, twoModifierFallback.metaState)
// No default button, fallback only.
- val keyOnlyFallback =
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0)
+ val keyOnlyFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0)
assertEquals(KeyEvent.KEYCODE_DPAD_CENTER, keyOnlyFallback.keyCode)
assertEquals(0, keyOnlyFallback.metaState)
// A key event that is not an exact match for a fallback. Expect a null return.
// E.g. Ctrl + Space -> LanguageSwitch
// Ctrl + Alt + Space -> Ctrl + Alt + Space (No fallback).
- val noMatchFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+ val noMatchFallback =
+ keyCharacterMap.getFallbackAction(
+ KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ )
assertNull(noMatchFallback)
}
}
diff --git a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
index cef985600c40..8fa5f4a03749 100644
--- a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
+++ b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
@@ -21,8 +21,8 @@ import org.junit.rules.ExternalResource
import org.mockito.Mockito
/**
- * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock
- * to be used for testing.
+ * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock to
+ * be used for testing.
*/
class MockInputManagerRule : ExternalResource() {
diff --git a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
index d3eeac147c2a..784eb1b37765 100644
--- a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
+++ b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
@@ -26,11 +26,10 @@ import android.view.MotionEvent.ACTION_MOVE
import android.view.MotionEvent.PointerCoords
import android.view.MotionEvent.PointerProperties
import android.view.MotionPredictor
-
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-
+import java.time.Duration
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
@@ -40,14 +39,12 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
-import java.time.Duration
-
private fun getStylusMotionEvent(
- eventTime: Duration,
- action: Int,
- x: Float,
- y: Float,
- ): MotionEvent{
+ eventTime: Duration,
+ action: Int,
+ x: Float,
+ y: Float,
+): MotionEvent {
val pointerCount = 1
val properties = arrayOfNulls<MotionEvent.PointerProperties>(pointerCount)
val coords = arrayOfNulls<MotionEvent.PointerCoords>(pointerCount)
@@ -61,21 +58,32 @@ private fun getStylusMotionEvent(
coords[i]!!.y = y
}
- return MotionEvent.obtain(/*downTime=*/0, eventTime.toMillis(), action, properties.size,
- properties, coords, /*metaState=*/0, /*buttonState=*/0,
- /*xPrecision=*/0f, /*yPrecision=*/0f, /*deviceId=*/0, /*edgeFlags=*/0,
- InputDevice.SOURCE_STYLUS, /*flags=*/0)
+ return MotionEvent.obtain(
+ /*downTime=*/ 0,
+ eventTime.toMillis(),
+ action,
+ properties.size,
+ properties,
+ coords,
+ /*metaState=*/ 0,
+ /*buttonState=*/ 0,
+ /*xPrecision=*/ 0f,
+ /*yPrecision=*/ 0f,
+ /*deviceId=*/ 0,
+ /*edgeFlags=*/ 0,
+ InputDevice.SOURCE_STYLUS,
+ /*flags=*/ 0,
+ )
}
private fun getPredictionContext(offset: Duration, enablePrediction: Boolean): Context {
val context = mock(Context::class.java)
val resources: Resources = mock(Resources::class.java)
`when`(context.getResources()).thenReturn(resources)
- `when`(resources.getInteger(
- com.android.internal.R.integer.config_motionPredictionOffsetNanos)).thenReturn(
- offset.toNanos().toInt())
- `when`(resources.getBoolean(
- com.android.internal.R.bool.config_enableMotionPrediction)).thenReturn(enablePrediction)
+ `when`(resources.getInteger(com.android.internal.R.integer.config_motionPredictionOffsetNanos))
+ .thenReturn(offset.toNanos().toInt())
+ `when`(resources.getBoolean(com.android.internal.R.bool.config_enableMotionPrediction))
+ .thenReturn(enablePrediction)
return context
}
@@ -88,38 +96,36 @@ class MotionPredictorTest {
@Before
fun setUp() {
instrumentation.uiAutomation.executeShellCommand(
- "setprop persist.input.enable_motion_prediction true")
+ "setprop persist.input.enable_motion_prediction true"
+ )
}
@After
fun tearDown() {
instrumentation.uiAutomation.executeShellCommand(
- "setprop persist.input.enable_motion_prediction $initialPropertyValue")
+ "setprop persist.input.enable_motion_prediction $initialPropertyValue"
+ )
}
/**
- * In a typical usage, app will send the event to the predictor and then call .predict to draw
- * a prediction. Here, we send 2 events to the predictor and check the returned event.
- * Input:
- * t = 0 x = 0 y = 0
- * t = 4 x = 10 y = 20
- * Output (expected):
- * t = 12 x = 30 y = 60 ± error
+ * In a typical usage, app will send the event to the predictor and then call .predict to draw a
+ * prediction. Here, we send 2 events to the predictor and check the returned event. Input: t =
+ * 0 x = 0 y = 0 t = 4 x = 10 y = 20 Output (expected): t = 12 x = 30 y = 60 ± error
*
* Historical data is ignored for simplicity.
*/
@Test
fun testPredictedCoordinatesAndTime() {
- val context = getPredictionContext(
- /*offset=*/Duration.ofMillis(1), /*enablePrediction=*/true)
+ val context =
+ getPredictionContext(/* offset= */ Duration.ofMillis(1), /* enablePrediction= */ true)
val predictor = MotionPredictor(context)
var eventTime = Duration.ofMillis(0)
- val downEvent = getStylusMotionEvent(eventTime, ACTION_DOWN, /*x=*/0f, /*y=*/0f)
+ val downEvent = getStylusMotionEvent(eventTime, ACTION_DOWN, /* x= */ 0f, /* y= */ 0f)
// ACTION_DOWN t=0 x=0 y=0
predictor.record(downEvent)
eventTime += Duration.ofMillis(4)
- val moveEvent = getStylusMotionEvent(eventTime, ACTION_MOVE, /*x=*/10f, /*y=*/20f)
+ val moveEvent = getStylusMotionEvent(eventTime, ACTION_MOVE, /* x= */ 10f, /* y= */ 20f)
// ACTION_MOVE t=1 x=1 y=2
predictor.record(moveEvent)
@@ -129,7 +135,7 @@ class MotionPredictorTest {
// Prediction will happen for t=12 (since it is the next input interval after the requested
// time, 8, plus the model offset, 1).
assertEquals(12, predicted!!.eventTime)
- assertEquals(30f, predicted.x, /*delta=*/10f)
- assertEquals(60f, predicted.y, /*delta=*/15f)
+ assertEquals(30f, predicted.x, /* delta= */ 10f)
+ assertEquals(60f, predicted.y, /* delta= */ 15f)
}
}
diff --git a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
index f311bc222d22..3c4ba5a3cd09 100644
--- a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
+++ b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
@@ -21,11 +21,10 @@ import android.view.InputChannel
import android.view.InputDevice
import android.view.MotionEvent
import android.view.WindowManagerPolicyConstants.PointerEventListener
-
import com.android.server.UiThread
import com.android.server.wm.PointerEventDispatcher
-import org.junit.Assert.assertEquals
import org.junit.After
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -39,6 +38,7 @@ class PointerEventDispatcherTest {
companion object {
private const val TAG = "PointerEventDispatcherTest"
}
+
private val mHandlerThread = HandlerThread("Process input events")
private lateinit var mSender: SpyInputEventSender
private lateinit var mPointerEventDispatcher: PointerEventDispatcher
@@ -75,8 +75,15 @@ class PointerEventDispatcherTest {
// The MotionEvent properties aren't important for this test, as long as the event
// is a pointer event, so that it gets processed by CrashingPointerEventListener
val downTime = 0L
- val motionEvent = MotionEvent.obtain(downTime, downTime,
- MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 0 /* metaState */)
+ val motionEvent =
+ MotionEvent.obtain(
+ downTime,
+ downTime,
+ MotionEvent.ACTION_DOWN,
+ 0f /* x */,
+ 0f /* y */,
+ 0, /* metaState */
+ )
motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
val seq = 10
mSender.sendInputEvent(seq, motionEvent)
diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
index abfe549f3d22..443ef6937c53 100644
--- a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
+++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
@@ -42,8 +42,7 @@ import platform.test.screenshot.matchers.PixelPerfectMatcher
/**
* Unit tests for PointerIcon.
*
- * Run with:
- * atest InputTests:com.android.test.input.PointerIconLoadingTest
+ * Run with: atest InputTests:com.android.test.input.PointerIconLoadingTest
*/
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -51,16 +50,19 @@ class PointerIconLoadingTest {
private lateinit var context: Context
private lateinit var exactScreenshotMatcher: BitmapMatcher
- @get:Rule
- val testName = TestName()
+ @get:Rule val testName = TestName()
@get:Rule
- val screenshotRule = ScreenshotTestRule(GoldenPathManager(
- InstrumentationRegistry.getInstrumentation().getContext(),
- ASSETS_PATH,
- TEST_OUTPUT_PATH,
- PathConfig()
- ), disableIconPool = false)
+ val screenshotRule =
+ ScreenshotTestRule(
+ GoldenPathManager(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ ASSETS_PATH,
+ TEST_OUTPUT_PATH,
+ PathConfig(),
+ ),
+ disableIconPool = false,
+ )
@Before
fun setUp() {
@@ -86,22 +88,26 @@ class PointerIconLoadingTest {
theme.setTo(context.getTheme())
theme.applyStyle(
PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN),
- /* force= */ true)
- theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(
- PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE), /* force= */ true)
+ /* force= */ true,
+ )
+ theme.applyStyle(
+ PointerIcon.vectorStrokeStyleToResource(
+ PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE
+ ),
+ /* force= */ true,
+ )
val pointerIcon =
PointerIcon.getLoadedSystemIcon(
ContextThemeWrapper(context, theme),
PointerIcon.TYPE_ARROW,
/* useLargeIcons= */ false,
- /* pointerScale= */ 1f)
+ /* pointerScale= */ 1f,
+ )
- pointerIcon.getBitmap().assertAgainstGolden(
- screenshotRule,
- testName.methodName,
- exactScreenshotMatcher
- )
+ pointerIcon
+ .getBitmap()
+ .assertAgainstGolden(screenshotRule, testName.methodName, exactScreenshotMatcher)
}
@Test
@@ -113,22 +119,26 @@ class PointerIconLoadingTest {
theme.setTo(context.getTheme())
theme.applyStyle(
PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK),
- /* force= */ true)
- theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(
- PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BLACK), /* force= */ true)
+ /* force= */ true,
+ )
+ theme.applyStyle(
+ PointerIcon.vectorStrokeStyleToResource(
+ PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BLACK
+ ),
+ /* force= */ true,
+ )
val pointerIcon =
PointerIcon.getLoadedSystemIcon(
ContextThemeWrapper(context, theme),
PointerIcon.TYPE_ARROW,
/* useLargeIcons= */ false,
- /* pointerScale= */ 1f)
+ /* pointerScale= */ 1f,
+ )
- pointerIcon.getBitmap().assertAgainstGolden(
- screenshotRule,
- testName.methodName,
- exactScreenshotMatcher
- )
+ pointerIcon
+ .getBitmap()
+ .assertAgainstGolden(screenshotRule, testName.methodName, exactScreenshotMatcher)
}
@Test
@@ -140,11 +150,14 @@ class PointerIconLoadingTest {
theme.setTo(context.getTheme())
theme.applyStyle(
PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK),
- /* force= */ true)
+ /* force= */ true,
+ )
theme.applyStyle(
PointerIcon.vectorStrokeStyleToResource(
- PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE),
- /* force= */ true)
+ PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE
+ ),
+ /* force= */ true,
+ )
val pointerScale = 2f
val pointerIcon =
@@ -152,13 +165,12 @@ class PointerIconLoadingTest {
ContextThemeWrapper(context, theme),
PointerIcon.TYPE_ARROW,
/* useLargeIcons= */ false,
- pointerScale)
+ pointerScale,
+ )
- pointerIcon.getBitmap().assertAgainstGolden(
- screenshotRule,
- testName.methodName,
- exactScreenshotMatcher
- )
+ pointerIcon
+ .getBitmap()
+ .assertAgainstGolden(screenshotRule, testName.methodName, exactScreenshotMatcher)
}
companion object {
diff --git a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
index 5cbfce534b15..05aa5e91132b 100644
--- a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
+++ b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
@@ -26,7 +26,6 @@ import android.view.KeyEvent
import android.view.MotionEvent
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
-
import org.junit.Assert.assertNull
private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T? {
@@ -39,7 +38,7 @@ private fun <T> assertNoEvents(queue: LinkedBlockingQueue<T>) {
}
class SpyInputEventReceiver(channel: InputChannel, looper: Looper) :
- InputEventReceiver(channel, looper) {
+ InputEventReceiver(channel, looper) {
private val mInputEvents = LinkedBlockingQueue<InputEvent>()
override fun onInputEvent(event: InputEvent) {
@@ -57,8 +56,9 @@ class SpyInputEventReceiver(channel: InputChannel, looper: Looper) :
}
class SpyInputEventSender(channel: InputChannel, looper: Looper) :
- InputEventSender(channel, looper) {
+ InputEventSender(channel, looper) {
data class FinishedSignal(val seq: Int, val handled: Boolean)
+
data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
index 1a0837b6d3d7..49b224a751b6 100644
--- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
+++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
@@ -59,9 +59,7 @@ import org.junit.runners.Parameterized
class UinputRecordingIntegrationTests {
companion object {
- /**
- * Add new test cases by adding a new [TestData] to the following list.
- */
+ /** Add new test cases by adding a new [TestData] to the following list. */
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun data(): Iterable<Any> =
@@ -74,12 +72,10 @@ class UinputRecordingIntegrationTests {
vendorId = 0x0603,
productId = 0x7806,
deviceSources = InputDevice.SOURCE_TOUCHSCREEN,
- ),
+ )
)
- /**
- * Use the debug mode to see the JSON-encoded received events in logcat.
- */
+ /** Use the debug mode to see the JSON-encoded received events in logcat. */
const val DEBUG_RECEIVED_EVENTS = false
const val INPUT_DEVICE_SOURCE_ALL = -1
@@ -101,14 +97,11 @@ class UinputRecordingIntegrationTests {
private lateinit var instrumentation: Instrumentation
private lateinit var parser: InputJsonParser
- @get:Rule
- val debugInputRule = DebugInputRule()
+ @get:Rule val debugInputRule = DebugInputRule()
- @get:Rule
- val testName = TestName()
+ @get:Rule val testName = TestName()
- @Parameterized.Parameter(0)
- lateinit var testData: TestData
+ @Parameterized.Parameter(0) lateinit var testData: TestData
@Before
fun setUp() {
@@ -120,43 +113,47 @@ class UinputRecordingIntegrationTests {
@Test
fun testEvemuRecording() {
VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>(
- testName,
- size = testData.displaySize
- ).use { scenario ->
- scenario.activity.window.decorView.requestUnbufferedDispatch(INPUT_DEVICE_SOURCE_ALL)
-
- EvemuDevice(
- instrumentation,
- testData.deviceSources,
- testData.vendorId,
- testData.productId,
- testData.uinputRecordingResource,
- scenario.virtualDisplay.display
- ).use { evemuDevice ->
-
- evemuDevice.injectEvents()
-
- if (DEBUG_RECEIVED_EVENTS) {
- printReceivedEventsToLogcat(scenario.activity)
- fail("Test cannot pass in debug mode!")
- }
-
- val verifier = EventVerifier(
- BatchedEventSplitter { scenario.activity.getInputEvent() }
+ testName,
+ size = testData.displaySize,
+ )
+ .use { scenario ->
+ scenario.activity.window.decorView.requestUnbufferedDispatch(
+ INPUT_DEVICE_SOURCE_ALL
)
- verifyEvents(verifier)
- scenario.activity.assertNoEvents()
+
+ EvemuDevice(
+ instrumentation,
+ testData.deviceSources,
+ testData.vendorId,
+ testData.productId,
+ testData.uinputRecordingResource,
+ scenario.virtualDisplay.display,
+ )
+ .use { evemuDevice ->
+ evemuDevice.injectEvents()
+
+ if (DEBUG_RECEIVED_EVENTS) {
+ printReceivedEventsToLogcat(scenario.activity)
+ fail("Test cannot pass in debug mode!")
+ }
+
+ val verifier =
+ EventVerifier(
+ BatchedEventSplitter { scenario.activity.getInputEvent() }
+ )
+ verifyEvents(verifier)
+ scenario.activity.assertNoEvents()
+ }
}
- }
}
private fun printReceivedEventsToLogcat(activity: CaptureEventActivity) {
val getNextEvent = BatchedEventSplitter { activity.getInputEvent() }
var receivedEvent: InputEvent? = getNextEvent()
while (receivedEvent != null) {
- Log.d(TAG,
- parser.encodeEvent(receivedEvent)?.toString()
- ?: "(Failed to encode received event)"
+ Log.d(
+ TAG,
+ parser.encodeEvent(receivedEvent)?.toString() ?: "(Failed to encode received event)",
)
receivedEvent = getNextEvent()
}
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index 1e44617af111..0366abed1d48 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -1,17 +1,15 @@
/**
* Copyright (c) 2020 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
+ * 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.
+ * 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.
*/
// InputMonitor is deprecated, but we still need to test it.
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
index 6ef1ecdae59b..8405a67332ef 100644
--- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -28,6 +28,7 @@ class ViewFrameInfoTest {
companion object {
private const val TAG = "ViewFrameInfoTest"
}
+
private val mViewFrameInfo = ViewFrameInfo()
private var mTimeStarted: Long = 0
@@ -65,8 +66,8 @@ class ViewFrameInfoTest {
// The values inside FrameInfo should match those from ViewFrameInfo after we update them
mViewFrameInfo.populateFrameInfo(frameInfo)
assertThat(frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID]).isEqualTo(139)
- assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
- FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED)
+ assertThat(frameInfo.frameInfo[FrameInfo.FLAGS])
+ .isEqualTo(FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED)
assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
}
-} \ No newline at end of file
+}