summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp22
-rw-r--r--core/api/current.txt48
-rw-r--r--core/api/system-current.txt13
-rw-r--r--core/java/android/accounts/AccountManager.java20
-rw-r--r--core/java/android/app/ActivityManager.java87
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java45
-rw-r--r--core/java/android/app/Notification.java39
-rw-r--r--core/java/android/app/ResourcesManager.java120
-rw-r--r--core/java/android/app/jank/FrameOverrunHistogram.java2
-rw-r--r--core/java/android/app/jank/JankDataProcessor.java74
-rw-r--r--core/java/android/app/jank/JankTracker.java7
-rw-r--r--core/java/android/app/jank/StateTracker.java6
-rw-r--r--core/java/android/content/Context.java2
-rw-r--r--core/java/android/content/ContextWrapper.java1
-rw-r--r--core/java/android/content/pm/ServiceInfo.java10
-rw-r--r--core/java/android/content/res/ApkAssets.java33
-rw-r--r--core/java/android/content/res/ResourcesImpl.java22
-rw-r--r--core/java/android/content/res/flags.aconfig9
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java42
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java5
-rw-r--r--core/java/android/os/PerformanceHintManager.java5
-rw-r--r--core/java/android/os/flags.aconfig9
-rw-r--r--core/java/android/permission/flags.aconfig9
-rw-r--r--core/java/android/provider/Settings.java23
-rw-r--r--core/java/android/service/carrier/CarrierMessagingService.java450
-rw-r--r--core/jni/android_os_PerformanceHintManager.cpp9
-rw-r--r--core/res/Android.bp1
-rw-r--r--core/res/AndroidManifest.xml16
-rw-r--r--core/tests/systemproperties/src/android/os/SystemPropertiesTest.java7
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml8
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt1
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp38
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml85
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml97
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto71
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt76
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt83
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt86
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt86
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt76
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt83
-rw-r--r--libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt86
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java86
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java9
-rw-r--r--libs/hwui/DeviceInfo.h1
-rw-r--r--libs/hwui/RenderNode.cpp2
-rw-r--r--libs/hwui/RenderProperties.h3
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp5
-rw-r--r--libs/hwui/tests/unit/RenderPropertiesTests.cpp8
-rw-r--r--libs/input/MouseCursorController.cpp32
-rw-r--r--libs/input/MouseCursorController.h7
-rw-r--r--libs/input/PointerController.cpp13
-rw-r--r--libs/input/PointerController.h4
-rw-r--r--libs/input/tests/PointerController_test.cpp131
-rw-r--r--media/java/android/media/AudioDevicePort.java5
-rw-r--r--media/java/android/media/quality/IMediaQualityManager.aidl2
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java11
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java16
-rw-r--r--media/java/android/media/tv/tuner/frontend/StandardExtension.java (renamed from media/java/android/media/tv/tuner/frontend/StandardExt.java)24
-rw-r--r--media/jni/android_media_tv_Tuner.cpp6
-rw-r--r--native/android/libandroid.map.txt3
-rw-r--r--native/android/performance_hint.cpp74
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java9
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java36
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java38
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig14
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt8
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt3
-rw-r--r--packages/SystemUI/customization/res/values-land/dimens.xml (renamed from libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml)13
-rw-r--r--packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/customization/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt167
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt138
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt14
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl4
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java38
-rw-r--r--packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt2
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java34
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java4
-rw-r--r--services/companion/java/com/android/server/companion/securechannel/SecureChannel.java4
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java25
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java11
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java27
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java13
-rw-r--r--services/core/java/com/android/server/wm/Task.java59
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java8
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java8
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java10
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java58
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java32
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java92
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt44
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml18
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java18
153 files changed, 3443 insertions, 1142 deletions
diff --git a/Android.bp b/Android.bp
index 54cb2684068d..49a6a2b3ec65 100644
--- a/Android.bp
+++ b/Android.bp
@@ -103,10 +103,10 @@ filegroup {
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
":android.hardware.keymaster-V4-java-source",
- ":android.hardware.radio-V3-java-source",
- ":android.hardware.radio.data-V3-java-source",
- ":android.hardware.radio.network-V3-java-source",
- ":android.hardware.radio.voice-V3-java-source",
+ ":android.hardware.radio-V4-java-source",
+ ":android.hardware.radio.data-V4-java-source",
+ ":android.hardware.radio.network-V4-java-source",
+ ":android.hardware.radio.voice-V4-java-source",
":android.hardware.security.secureclock-V1-java-source",
":android.hardware.thermal-V3-java-source",
":android.hardware.tv.tuner-V3-java-source",
@@ -232,13 +232,13 @@ java_library {
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
"android.hardware.radio-V1.6-java",
- "android.hardware.radio.data-V3-java",
- "android.hardware.radio.ims-V2-java",
- "android.hardware.radio.messaging-V3-java",
- "android.hardware.radio.modem-V3-java",
- "android.hardware.radio.network-V3-java",
- "android.hardware.radio.sim-V3-java",
- "android.hardware.radio.voice-V3-java",
+ "android.hardware.radio.data-V4-java",
+ "android.hardware.radio.ims-V3-java",
+ "android.hardware.radio.messaging-V4-java",
+ "android.hardware.radio.modem-V4-java",
+ "android.hardware.radio.network-V4-java",
+ "android.hardware.radio.sim-V4-java",
+ "android.hardware.radio.voice-V4-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V1.0-java",
"android.hardware.thermal-V1.1-java",
diff --git a/core/api/current.txt b/core/api/current.txt
index 5a7f2bdb2949..76f3e5a3cd39 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3830,7 +3830,7 @@ package android.accounts {
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public boolean notifyAccountAuthenticated(android.accounts.Account);
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public String peekAuthToken(android.accounts.Account, String);
method @Deprecated @RequiresPermission(value="android.permission.MANAGE_ACCOUNTS", apis="..22") public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
- method @RequiresPermission(value="android.permission.MANAGE_ACCOUNTS", apis="..22") public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @RequiresPermission(value="android.permission.REMOVE_ACCOUNTS", conditional=true) public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public boolean removeAccountExplicitly(android.accounts.Account);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
method @RequiresPermission(value="android.permission.AUTHENTICATE_ACCOUNTS", apis="..22") public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, @Size(min=1) String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
@@ -13809,7 +13809,7 @@ package android.content.pm {
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING, android.Manifest.permission.RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
- field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
+ field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS, android.health.connect.HealthPermissions.READ_HEART_RATE, android.health.connect.HealthPermissions.READ_SKIN_TEMPERATURE, android.health.connect.HealthPermissions.READ_OXYGEN_SATURATION}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
@@ -21214,6 +21214,7 @@ package android.inputmethodservice {
method public void setExtractView(android.view.View);
method public void setExtractViewShown(boolean);
method public void setInputView(android.view.View);
+ method @FlaggedApi("android.view.inputmethod.adaptive_handwriting_bounds") public final void setStylusHandwritingRegion(@NonNull android.graphics.Region);
method public final void setStylusHandwritingSessionTimeout(@NonNull java.time.Duration);
method public final boolean shouldOfferSwitchingToNextInputMethod();
method public void showStatusIcon(@DrawableRes int);
@@ -41180,6 +41181,18 @@ package android.service.carrier {
method @Deprecated public void onSendTextSms(@NonNull String, int, @NonNull String, @NonNull android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
method public void onSendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 606; // 0x25e
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED = 610; // 0x262
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE = 603; // 0x25b
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 609; // 0x261
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN = 601; // 0x259
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 608; // 0x260
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR = 604; // 0x25c
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 611; // 0x263
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK = 607; // 0x25f
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_RETRY = 605; // 0x25d
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 602; // 0x25a
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED = 600; // 0x258
field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0
field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0
@@ -41187,7 +41200,38 @@ package android.service.carrier {
field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE = 2; // 0x2
field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1
field public static final int SEND_STATUS_ERROR = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 406; // 0x196
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_DATA_DISABLED = 410; // 0x19a
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_HTTP_FAILURE = 403; // 0x193
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 409; // 0x199
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INVALID_APN = 401; // 0x191
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 408; // 0x198
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_IO_ERROR = 404; // 0x194
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 411; // 0x19b
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK = 407; // 0x197
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_RETRY = 405; // 0x195
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 402; // 0x192
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_UNSPECIFIED = 400; // 0x190
field public static final int SEND_STATUS_OK = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_CANCELLED = 215; // 0xd7
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ENCODING_ERROR = 212; // 0xd4
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE = 204; // 0xcc
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE = 200; // 0xc8
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED = 203; // 0xcb
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_NO_SERVICE = 202; // 0xca
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_NULL_PDU = 201; // 0xc9
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 206; // 0xce
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 205; // 0xcd
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_ARGUMENTS = 208; // 0xd0
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS = 213; // 0xd5
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_SMS_FORMAT = 210; // 0xd2
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_STATE = 209; // 0xd1
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_NETWORK_ERROR = 211; // 0xd3
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_NETWORK_REJECT = 207; // 0xcf
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED = 214; // 0xd6
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED = 216; // 0xd8
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY = 217; // 0xd9
+ field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED = 218; // 0xda
field public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
field public static final String SERVICE_INTERFACE = "android.service.carrier.CarrierMessagingService";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 14a6c8c4928d..a171f3dc4c17 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -135,6 +135,7 @@ package android {
field public static final String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
field public static final String CONTROL_OEM_PAID_NETWORK_PREFERENCE = "android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE";
field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN";
+ field @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") public static final String COPY_ACCOUNTS = "android.permission.COPY_ACCOUNTS";
field public static final String CREATE_USERS = "android.permission.CREATE_USERS";
field public static final String CREATE_VIRTUAL_DEVICE = "android.permission.CREATE_VIRTUAL_DEVICE";
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
@@ -347,6 +348,7 @@ package android {
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
+ field @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") public static final String REMOVE_ACCOUNTS = "android.permission.REMOVE_ACCOUNTS";
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
@@ -572,6 +574,7 @@ package android.accessibilityservice {
package android.accounts {
public class AccountManager {
+ method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.COPY_ACCOUNTS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public android.accounts.AccountManagerFuture<java.lang.Boolean> copyAccountToUser(@NonNull android.accounts.Account, @NonNull android.os.UserHandle, @NonNull android.os.UserHandle, @Nullable android.accounts.AccountManagerCallback<java.lang.Boolean>, @Nullable android.os.Handler);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.accounts.AccountManagerFuture<android.os.Bundle> finishSessionAsUser(android.os.Bundle, android.app.Activity, android.os.UserHandle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
}
@@ -9896,7 +9899,7 @@ package android.media.tv.tuner.frontend {
method public int getSignalStrength();
method public int getSnr();
method public int getSpectralInversion();
- method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExt getStandardExt();
+ method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExtension getStandardExtension();
method @NonNull public int[] getStreamIds();
method public int getSymbolRate();
method @IntRange(from=0, to=65535) public int getSystemId();
@@ -9951,7 +9954,7 @@ package android.media.tv.tuner.frontend {
field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
- field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT = 47; // 0x2f
+ field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXTENSION = 47; // 0x2f
field public static final int FRONTEND_STATUS_TYPE_STREAM_IDS = 39; // 0x27
field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
@@ -10243,9 +10246,9 @@ package android.media.tv.tuner.frontend {
method public default void onUnlocked();
}
- @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExt {
- method public int getDvbsStandardExt();
- method public int getDvbtStandardExt();
+ @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExtension {
+ method public int getDvbsStandardExtension();
+ method public int getDvbtStandardExtension();
}
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 87acbbf65b2f..72450999993d 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,9 +16,13 @@
package android.accounts;
+import static android.Manifest.permission.COPY_ACCOUNTS;
+import static android.Manifest.permission.REMOVE_ACCOUNTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED;
import android.annotation.BroadcastBehavior;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +30,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.Size;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
@@ -1312,7 +1317,8 @@ public class AccountManager {
* {@link AccountManagerFuture} must not be used on the main thread.
*
* <p>This method requires the caller to have a signature match with the
- * authenticator that manages the specified account.
+ * authenticator that manages the specified account, be a profile owner or have the
+ * {@link android.Manifest.permission#REMOVE_ACCOUNTS} permission.
*
* <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
* MANAGE_ACCOUNTS permission is needed for those platforms. See docs for
@@ -1344,6 +1350,8 @@ public class AccountManager {
* </ul>
*/
@UserHandleAware
+ @RequiresPermission(value = REMOVE_ACCOUNTS, conditional = true)
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
public AccountManagerFuture<Bundle> removeAccount(final Account account,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
return removeAccountAsUser(account, activity, callback, handler, mContext.getUser());
@@ -2019,9 +2027,15 @@ public class AccountManager {
* succeeded.
* @hide
*/
+ @SuppressLint("SamShouldBeLast")
+ @NonNull
+ @SystemApi
+ @RequiresPermission(anyOf = {COPY_ACCOUNTS, INTERACT_ACROSS_USERS_FULL})
+ @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
public AccountManagerFuture<Boolean> copyAccountToUser(
- final Account account, final UserHandle fromUser, final UserHandle toUser,
- AccountManagerCallback<Boolean> callback, Handler handler) {
+ @NonNull final Account account, @NonNull final UserHandle fromUser,
+ @NonNull final UserHandle toUser, @Nullable AccountManagerCallback<Boolean> callback,
+ @Nullable Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
if (toUser == null || fromUser == null) {
throw new IllegalArgumentException("fromUser and toUser cannot be null");
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ab75069cc5d8..33ba05865042 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2679,62 +2679,6 @@ public class ActivityManager {
*/
public static class RecentTaskInfo extends TaskInfo implements Parcelable {
/**
- * @hide
- */
- public static class PersistedTaskSnapshotData {
- /**
- * The bounds of the task when the last snapshot was taken, may be null if the task is
- * not yet attached to the hierarchy.
- * @see {@link android.window.TaskSnapshot#mTaskSize}.
- * @hide
- */
- public @Nullable Point taskSize;
-
- /**
- * The content insets of the task when the task snapshot was taken.
- * @see {@link android.window.TaskSnapshot#mContentInsets}.
- * @hide
- */
- public @Nullable Rect contentInsets;
-
- /**
- * The size of the last snapshot taken, may be null if there is no associated snapshot.
- * @see {@link android.window.TaskSnapshot#mSnapshot}.
- * @hide
- */
- public @Nullable Point bufferSize;
-
- /**
- * Sets the data from the other data.
- * @hide
- */
- public void set(PersistedTaskSnapshotData other) {
- taskSize = other.taskSize;
- contentInsets = other.contentInsets;
- bufferSize = other.bufferSize;
- }
-
- /**
- * Sets the data from the provided {@param snapshot}.
- * @hide
- */
- public void set(TaskSnapshot snapshot) {
- if (snapshot == null) {
- taskSize = null;
- contentInsets = null;
- bufferSize = null;
- return;
- }
- final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- taskSize = new Point(snapshot.getTaskSize());
- contentInsets = new Rect(snapshot.getContentInsets());
- bufferSize = buffer != null
- ? new Point(buffer.getWidth(), buffer.getHeight())
- : null;
- }
- }
-
- /**
* If this task is currently running, this is the identifier for it.
* If it is not running, this will be -1.
*
@@ -2770,24 +2714,6 @@ public class ActivityManager {
@Deprecated
public int affiliatedTaskId;
- /**
- * Information of organized child tasks.
- *
- * @deprecated No longer used
- * @hide
- */
- @Deprecated
- public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>();
-
- /**
- * Information about the last snapshot taken for this task.
- *
- * @deprecated No longer used
- * @hide
- */
- @Deprecated
- public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
-
public RecentTaskInfo() {
}
@@ -2803,10 +2729,6 @@ public class ActivityManager {
public void readFromParcel(Parcel source) {
id = source.readInt();
persistentId = source.readInt();
- childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader(), android.app.ActivityManager.RecentTaskInfo.class);
- lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR);
- lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR);
- lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR);
super.readTaskFromParcel(source);
}
@@ -2814,10 +2736,6 @@ public class ActivityManager {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(persistentId);
- dest.writeList(childrenTaskInfos);
- dest.writeTypedObject(lastSnapshotData.taskSize, flags);
- dest.writeTypedObject(lastSnapshotData.contentInsets, flags);
- dest.writeTypedObject(lastSnapshotData.bufferSize, flags);
super.writeTaskToParcel(dest, flags);
}
@@ -2884,11 +2802,6 @@ public class ActivityManager {
pw.println(" }");
}
pw.print(" ");
- pw.print(" lastSnapshotData {");
- pw.print(" taskSize=" + lastSnapshotData.taskSize);
- pw.print(" contentInsets=" + lastSnapshotData.contentInsets);
- pw.print(" bufferSize=" + lastSnapshotData.bufferSize);
- pw.println(" }");
}
}
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 16444dc5adde..6efc4ef55180 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -62,6 +62,7 @@ import android.content.pm.ServiceInfo.ForegroundServiceType;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.health.connect.HealthPermissions;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -484,21 +485,35 @@ public abstract class ForegroundServiceTypePolicy {
*/
public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_HEALTH =
new ForegroundServiceTypePolicyInfo(
- FOREGROUND_SERVICE_TYPE_HEALTH,
- ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
- ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
- new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
- new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH)
- }, true),
- new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
- new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION),
- new RegularPermission(Manifest.permission.BODY_SENSORS),
- new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
- }, false),
- FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
- true /* permissionEnforcementFlagDefaultValue */,
- false /* foregroundOnlyPermission */
- );
+ FOREGROUND_SERVICE_TYPE_HEALTH,
+ ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
+ ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
+ new ForegroundServiceTypePermissions(
+ new ForegroundServiceTypePermission[] {
+ new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH)
+ },
+ true),
+ new ForegroundServiceTypePermissions(getAllowedHealthPermissions(), false),
+ FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
+ true /* permissionEnforcementFlagDefaultValue */,
+ false /* foregroundOnlyPermission */);
+
+ /** Returns the permissions needed for the policy of the health foreground service type. */
+ private static ForegroundServiceTypePermission[] getAllowedHealthPermissions() {
+ final ArrayList<ForegroundServiceTypePermission> permissions = new ArrayList<>();
+ permissions.add(new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION));
+ permissions.add(new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS));
+
+ if (android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) {
+ permissions.add(new RegularPermission(HealthPermissions.READ_HEART_RATE));
+ permissions.add(new RegularPermission(HealthPermissions.READ_SKIN_TEMPERATURE));
+ permissions.add(new RegularPermission(HealthPermissions.READ_OXYGEN_SATURATION));
+ } else {
+ permissions.add(new RegularPermission(Manifest.permission.BODY_SENSORS));
+ }
+
+ return permissions.toArray(new ForegroundServiceTypePermission[permissions.size()]);
+ }
/**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ff73382c43e6..b78f11148178 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3257,7 +3257,7 @@ public class Notification implements Parcelable
*/
@FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
public boolean hasPromotableCharacteristics() {
- return isColorized()
+ return isColorizedRequested()
&& hasTitle()
&& !containsCustomViews()
&& hasPromotableStyle();
@@ -4083,6 +4083,12 @@ public class Notification implements Parcelable
flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
}
}
+ if (Flags.apiRichOngoing()) {
+ if ((flags & FLAG_PROMOTED_ONGOING) != 0) {
+ flagStrings.add("PROMOTED_ONGOING");
+ flags &= ~FLAG_PROMOTED_ONGOING;
+ }
+ }
if (android.service.notification.Flags.notificationSilentFlag()) {
if ((flags & FLAG_SILENT) != 0) {
@@ -7792,8 +7798,16 @@ public class Notification implements Parcelable
* @hide
*/
public boolean isColorized() {
- return extras.getBoolean(EXTRA_COLORIZED)
- && (hasColorizedPermission() || isFgsOrUij());
+ return isColorizedRequested()
+ && (hasColorizedPermission() || isFgsOrUij() || isPromotedOngoing());
+ }
+
+ /**
+ * @return true if this notification has requested to be colorized, regardless of whether it
+ * meets the requirements to be displayed that way.
+ */
+ private boolean isColorizedRequested() {
+ return extras.getBoolean(EXTRA_COLORIZED);
}
/**
@@ -7807,6 +7821,19 @@ public class Notification implements Parcelable
}
/**
+ * Returns whether this notification is a promoted ongoing notification.
+ *
+ * This requires the Notification.FLAG_PROMOTED_ONGOING flag to be set
+ * (which may be true once the api_rich_ongoing feature flag is enabled),
+ * and requires that the ui_rich_ongoing feature flag is enabled.
+ *
+ * @hide
+ */
+ public boolean isPromotedOngoing() {
+ return Flags.uiRichOngoing() && (flags & Notification.FLAG_PROMOTED_ONGOING) != 0;
+ }
+
+ /**
* @return true if this is a media style notification with a media session
*
* @hide
@@ -11671,8 +11698,10 @@ public class Notification implements Parcelable
return points;
}
- @NonNull
- private NotificationProgressModel createProgressModel(int defaultProgressColor,
+ /**
+ * @hide
+ */
+ public @NonNull NotificationProgressModel createProgressModel(int defaultProgressColor,
int backgroundColor) {
final NotificationProgressModel model;
if (mIndeterminate) {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 087e246e8841..599a46b131d5 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -174,54 +174,22 @@ public class ResourcesManager {
}
/**
- * Apply the registered library paths to the passed AssetManager. If may create a new
- * AssetManager if any changes are needed and it isn't allowed to reuse the old one.
- *
- * @return new AssetManager and the hash code for the current version of the registered paths
+ * Apply the registered library paths to the passed impl object
+ * @return the hash code for the current version of the registered paths
*/
- public @NonNull Pair<AssetManager, Integer> updateResourceImplAssetsWithRegisteredLibs(
- @NonNull AssetManager assets, boolean reuseAssets) {
+ public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
if (!Flags.registerResourcePaths()) {
- return new Pair<>(assets, 0);
+ return 0;
}
- final int size;
- final PathCollector collector;
-
- synchronized (mLock) {
- size = mSharedLibAssetsMap.size();
- if (assets == AssetManager.getSystem()) {
- return new Pair<>(assets, size);
- }
- collector = new PathCollector(resourcesKeyFromAssets(assets));
- for (int i = 0; i < size; i++) {
- final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
- collector.appendKey(libraryKey);
- }
- }
- if (collector.isSameAsOriginal()) {
- return new Pair<>(assets, size);
- }
- if (reuseAssets) {
- assets.addPresetApkKeys(extractApkKeys(collector.collectedKey()));
- return new Pair<>(assets, size);
- }
- final var newAssetsBuilder = new AssetManager.Builder();
- for (final var asset : assets.getApkAssets()) {
- if (!asset.isForLoader()) {
- newAssetsBuilder.addApkAssets(asset);
- }
+ final var collector = new PathCollector(null);
+ final int size = mSharedLibAssetsMap.size();
+ for (int i = 0; i < size; i++) {
+ final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+ collector.appendKey(libraryKey);
}
- for (final var key : extractApkKeys(collector.collectedKey())) {
- try {
- final var asset = loadApkAssets(key);
- newAssetsBuilder.addApkAssets(asset);
- } catch (IOException e) {
- Log.e(TAG, "Couldn't load assets for key " + key, e);
- }
- }
- assets.getLoaders().forEach(newAssetsBuilder::addLoader);
- return new Pair<>(newAssetsBuilder.build(), size);
+ impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+ return size;
}
public static class ApkKey {
@@ -656,23 +624,6 @@ public class ResourcesManager {
return apkKeys;
}
- private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) {
- final var libs = new ArrayList<String>();
- final var overlays = new ArrayList<String>();
- for (final ApkAssets asset : assets.getApkAssets()) {
- if (asset.isSystem() || asset.isForLoader()) {
- continue;
- }
- if (asset.isOverlay()) {
- overlays.add(asset.getAssetPath());
- } else if (asset.isSharedLib()) {
- libs.add(asset.getAssetPath());
- }
- }
- return new ResourcesKey(null, null, overlays.toArray(new String[0]),
- libs.toArray(new String[0]), 0, null, null);
- }
-
/**
* Creates an AssetManager from the paths within the ResourcesKey.
*
@@ -801,7 +752,7 @@ public class ResourcesManager {
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
- final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true);
+ final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -1881,32 +1832,31 @@ public class ResourcesManager {
for (int i = 0; i < resourcesCount; i++) {
final WeakReference<Resources> ref = mAllResourceReferences.get(i);
final Resources r = ref != null ? ref.get() : null;
- if (r == null) {
- continue;
- }
- final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
- if (key != null) {
- final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
- if (impl == null) {
- throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
- }
- r.setImpl(impl);
- } else {
- // ResourcesKey is null which means the ResourcesImpl could belong to a
- // Resources created by application through Resources constructor and was not
- // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
- // have shared library asset paths appended if there are any.
- final ResourcesImpl oldImpl = r.getImpl();
- if (oldImpl != null) {
- final AssetManager oldAssets = oldImpl.getAssets();
- // ResourcesImpl constructor will help to append shared library asset paths.
- if (oldAssets != AssetManager.getSystem()) {
- if (oldAssets.isUpToDate()) {
- final ResourcesImpl newImpl = new ResourcesImpl(oldImpl);
+ if (r != null) {
+ final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+ if (key != null) {
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+ }
+ r.setImpl(impl);
+ } else {
+ // ResourcesKey is null which means the ResourcesImpl could belong to a
+ // Resources created by application through Resources constructor and was not
+ // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
+ // have shared library asset paths appended if there are any.
+ if (r.getImpl() != null) {
+ final ResourcesImpl oldImpl = r.getImpl();
+ final AssetManager oldAssets = oldImpl.getAssets();
+ // ResourcesImpl constructor will help to append shared library asset paths.
+ if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
+ oldImpl.getMetrics(), oldImpl.getConfiguration(),
+ oldImpl.getDisplayAdjustments());
r.setImpl(newImpl);
} else {
- Slog.w(TAG, "Skip appending shared library asset paths for "
- + "the Resources as its assets are not up to date.");
+ Slog.w(TAG, "Skip appending shared library asset paths for the "
+ + "Resource as its assets are not up to date.");
}
}
}
diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java
index e28ac126a90a..3ad6531a46bf 100644
--- a/core/java/android/app/jank/FrameOverrunHistogram.java
+++ b/core/java/android/app/jank/FrameOverrunHistogram.java
@@ -39,7 +39,7 @@ public class FrameOverrunHistogram {
* Create a new instance of FrameOverrunHistogram.
*/
public FrameOverrunHistogram() {
- mBucketCounts = new int[sBucketEndpoints.length - 1];
+ mBucketCounts = new int[sBucketEndpoints.length];
}
/**
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7525d0402ee4..7ceaeb3fb070 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -59,7 +59,6 @@ public class JankDataProcessor {
* @param appUid the uid of the app.
*/
public void processJankData(List<JankData> jankData, String activityName, int appUid) {
- mCurrentBatchCount++;
// add all the previous and active states to the pending states list.
mStateTracker.retrieveAllStates(mPendingStates);
@@ -79,9 +78,8 @@ public class JankDataProcessor {
}
}
// At this point we have attributed all frames to a state.
- if (mCurrentBatchCount >= LOG_BATCH_FREQUENCY) {
- logMetricCounts();
- }
+ incrementBatchCountAndMaybeLogStats();
+
// return the StatData object back to the pool to be reused.
jankDataProcessingComplete();
}
@@ -91,7 +89,73 @@ public class JankDataProcessor {
* stats
*/
public void mergeJankStats(AppJankStats jankStats, String activityName) {
- // TODO b/377572463 Add Merging Logic
+ // Each state has a key which is a combination of widget category, widget id and widget
+ // state, this key is also used to identify pending stats, a pending stat is essentially a
+ // state with frames associated with it.
+ String stateKey = mStateTracker.getStateKey(jankStats.getWidgetCategory(),
+ jankStats.getWidgetId(), jankStats.getWidgetState());
+
+ if (mPendingJankStats.containsKey(stateKey)) {
+ mergeExistingStat(stateKey, jankStats);
+ } else {
+ mergeNewStat(stateKey, activityName, jankStats);
+ }
+
+ incrementBatchCountAndMaybeLogStats();
+ }
+
+ private void mergeExistingStat(String stateKey, AppJankStats jankStat) {
+ PendingJankStat pendingStat = mPendingJankStats.get(stateKey);
+
+ pendingStat.mJankyFrames += jankStat.getJankyFrameCount();
+ pendingStat.mTotalFrames += jankStat.getTotalFrameCount();
+
+ mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
+ jankStat.getFrameOverrunHistogram().getBucketCounters());
+ }
+
+ private void mergeNewStat(String stateKey, String activityName, AppJankStats jankStats) {
+ // Check if we have space for a new stat
+ if (mPendingJankStats.size() > MAX_IN_MEMORY_STATS) {
+ return;
+ }
+
+ PendingJankStat pendingStat = mPendingJankStatsPool.acquire();
+ if (pendingStat == null) {
+ pendingStat = new PendingJankStat();
+
+ }
+ pendingStat.clearStats();
+
+ pendingStat.mActivityName = activityName;
+ pendingStat.mUid = jankStats.getUid();
+ pendingStat.mWidgetId = jankStats.getWidgetId();
+ pendingStat.mWidgetCategory = jankStats.getWidgetCategory();
+ pendingStat.mWidgetState = jankStats.getWidgetState();
+ pendingStat.mTotalFrames = jankStats.getTotalFrameCount();
+ pendingStat.mJankyFrames = jankStats.getJankyFrameCount();
+
+ mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
+ jankStats.getFrameOverrunHistogram().getBucketCounters());
+
+ mPendingJankStats.put(stateKey, pendingStat);
+ }
+
+ private void mergeOverrunHistograms(int[] mergeTarget, int[] mergeSource) {
+ // The length of each histogram should be identical, if they are not then its possible the
+ // buckets are not in sync, these records should not be recorded.
+ if (mergeTarget.length != mergeSource.length) return;
+
+ for (int i = 0; i < mergeTarget.length; i++) {
+ mergeTarget[i] += mergeSource[i];
+ }
+ }
+
+ private void incrementBatchCountAndMaybeLogStats() {
+ mCurrentBatchCount++;
+ if (mCurrentBatchCount >= LOG_BATCH_FREQUENCY) {
+ logMetricCounts();
+ }
}
/**
diff --git a/core/java/android/app/jank/JankTracker.java b/core/java/android/app/jank/JankTracker.java
index 202281f98c97..469521668d25 100644
--- a/core/java/android/app/jank/JankTracker.java
+++ b/core/java/android/app/jank/JankTracker.java
@@ -89,7 +89,12 @@ public class JankTracker {
* stats
*/
public void mergeAppJankStats(AppJankStats appJankStats) {
- mJankDataProcessor.mergeJankStats(appJankStats, mActivityName);
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ mJankDataProcessor.mergeJankStats(appJankStats, mActivityName);
+ }
+ });
}
public void setActivityName(@NonNull String activityName) {
diff --git a/core/java/android/app/jank/StateTracker.java b/core/java/android/app/jank/StateTracker.java
index c86d5a5cff20..21bb5e8280ee 100644
--- a/core/java/android/app/jank/StateTracker.java
+++ b/core/java/android/app/jank/StateTracker.java
@@ -180,7 +180,11 @@ public class StateTracker {
}
}
- private String getStateKey(String widgetCategory, String widgetId, String widgetState) {
+ /**
+ * Returns a concatenated string of the inputs. This key can be used to retrieve both pending
+ * stats and the state that was used to create the pending stat.
+ */
+ public String getStateKey(String widgetCategory, String widgetId, String widgetState) {
return widgetCategory + widgetId + widgetState;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5754e7e30c34..19cd2e69cfdf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4529,7 +4529,6 @@ public abstract class Context {
* @see #AUTHENTICATION_POLICY_SERVICE
* @see android.security.authenticationpolicy.AuthenticationPolicyManager
*/
- // TODO(b/347269120): Re-add @Nullable
public abstract Object getSystemService(@ServiceName @NonNull String name);
/**
@@ -4576,7 +4575,6 @@ public abstract class Context {
*/
@SuppressWarnings("unchecked")
@RavenwoodKeep
- // TODO(b/347269120): Re-add @Nullable
public final <T> T getSystemService(@NonNull Class<T> serviceClass) {
// Because subclasses may override getSystemService(String) we cannot
// perform a lookup by class alone. We must first map the class to its
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 23d17cb5ce50..413eb9886392 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -960,7 +960,6 @@ public class ContextWrapper extends Context {
}
@Override
- // TODO(b/347269120): Re-add @Nullable
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 4285b0a2b91a..8243d88e6260 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
+import android.health.connect.HealthPermissions;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Printer;
@@ -361,8 +362,10 @@ public class ServiceInfo extends ComponentInfo
* {@link android.Manifest.permission#FOREGROUND_SERVICE_HEALTH} and one of the following
* permissions:
* {@link android.Manifest.permission#ACTIVITY_RECOGNITION},
- * {@link android.Manifest.permission#BODY_SENSORS},
* {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}.
+ * {@link android.health.connect.HealthPermissions#READ_HEART_RATE},
+ * {@link android.health.connect.HealthPermissions#READ_SKIN_TEMPERATURE},
+ * {@link android.health.connect.HealthPermissions#READ_OXYGEN_SATURATION},
*/
@RequiresPermission(
allOf = {
@@ -370,10 +373,13 @@ public class ServiceInfo extends ComponentInfo
},
anyOf = {
Manifest.permission.ACTIVITY_RECOGNITION,
- Manifest.permission.BODY_SENSORS,
Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
+ HealthPermissions.READ_HEART_RATE,
+ HealthPermissions.READ_SKIN_TEMPERATURE,
+ HealthPermissions.READ_OXYGEN_SATURATION,
}
)
+ @FlaggedApi(android.permission.flags.Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 1 << 8;
/**
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 908999b64961..68b5d782bfbf 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -124,13 +124,11 @@ public final class ApkAssets {
@Nullable
@GuardedBy("this")
- private StringBlock mStringBlock; // null or closed if mNativePtr = 0.
+ private final StringBlock mStringBlock; // null or closed if mNativePtr = 0.
@PropertyFlags
private final int mFlags;
- private final boolean mIsOverlay;
-
@Nullable
private final AssetsProvider mAssets;
@@ -304,43 +302,40 @@ public final class ApkAssets {
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
- this(format, flags, assets);
Objects.requireNonNull(path, "path");
+ mFlags = flags;
mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
throws IOException {
- this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
+ mFlags = flags;
mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
- this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
+ mFlags = flags;
mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
- this(FORMAT_APK, flags, assets);
+ mFlags = flags;
mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
- }
-
- private ApkAssets(@FormatType int format, @PropertyFlags int flags,
- @Nullable AssetsProvider assets) {
- mFlags = flags;
mAssets = assets;
- mIsOverlay = format == FORMAT_IDMAP;
}
@UnsupportedAppUsage
@@ -430,18 +425,6 @@ public final class ApkAssets {
}
}
- public boolean isSystem() {
- return (mFlags & PROPERTY_SYSTEM) != 0;
- }
-
- public boolean isSharedLib() {
- return (mFlags & PROPERTY_DYNAMIC) != 0;
- }
-
- public boolean isOverlay() {
- return mIsOverlay;
- }
-
@Override
public String toString() {
return "ApkAssets{path=" + getDebugName() + "}";
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index bcaceb24d767..e6b93427f413 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -203,25 +203,9 @@ public class ResourcesImpl {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
- // Don't reuse assets by default as we have no control over whether they're already
- // inside some other ResourcesImpl.
- this(assets, metrics, config, displayAdjustments, false);
- }
-
- public ResourcesImpl(@NonNull ResourcesImpl orig) {
- // We know for sure that the other assets are in use, so can't reuse the object here.
- this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(),
- orig.getDisplayAdjustments(), false);
- }
-
- public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
- @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments,
- boolean reuseAssets) {
- final var assetsAndHash =
- ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets,
- reuseAssets);
- mAssets = assetsAndHash.first;
- mAppliedSharedLibsHash = assetsAndHash.second;
+ mAssets = assets;
+ mAppliedSharedLibsHash =
+ ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index f23c193e2da0..6fc7d90a8237 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -105,3 +105,12 @@ flag {
# This flag is used to control aapt2 behavior.
is_fixed_read_only: true
}
+
+flag {
+ name: "resources_minor_version_support"
+ is_exported: true
+ namespace: "resource_manager"
+ description: "Feature flag for supporting minor version in Resources"
+ bug: "373535266"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index dadb5c386b76..977c5bd927cf 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -407,6 +407,12 @@ public class InputMethodService extends AbstractInputMethodService {
private boolean mUsingCtrlShiftShortcut = false;
/**
+ * Last handwriting bounds used for stylus handwriting
+ * {@link #setStylusHandwritingRegion(Region)}.
+ */
+ private Region mLastHandwritingRegion;
+
+ /**
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
* the IME switcher button or not when the gestural navigation is enabled.
*
@@ -1532,6 +1538,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
editorInfo.makeCompatible(getApplicationInfo().targetSdkVersion);
+ mLastHandwritingRegion = null;
getInputMethodInternal().restartInput(new RemoteInputConnection(ric, sessionId),
editorInfo);
}
@@ -2840,6 +2847,7 @@ public class InputMethodService extends AbstractInputMethodService {
mHandler.removeCallbacks(mFinishHwRunnable);
}
mFinishHwRunnable = null;
+ mLastHandwritingRegion = null;
final int requestId = mHandwritingRequestId.getAsInt();
mHandwritingRequestId = OptionalInt.empty();
@@ -3166,6 +3174,40 @@ public class InputMethodService extends AbstractInputMethodService {
registerDefaultOnBackInvokedCallback();
}
+ /**
+ * Sets a new stylus handwriting region as user continues to write on an editor on screen.
+ * Stylus strokes that are started within the {@code touchableRegion} are treated as
+ * continuation of handwriting and all the events outside are passed-through to the IME target
+ * app, causing stylus handwriting to finish {@link #finishStylusHandwriting()}.
+ * By default, {@link WindowManager#getMaximumWindowMetrics()} is handwritable and
+ * {@code touchableRegion} resets after each handwriting session.
+ * <p>
+ * For example, the IME can use this API to dynamically expand the stylus handwriting region on
+ * every stylus stroke as user continues to write on an editor. The region should grow around
+ * the last stroke so that a UI element below the IME window is still interactable when it is
+ * spaced sufficiently away (~2 character dimensions) from last stroke.
+ * </p>
+ * <p>
+ * Note: Setting handwriting touchable region is supported on IMEs that support stylus
+ * handwriting {@link InputMethodInfo#supportsStylusHandwriting()}.
+ * </p>
+ *
+ * @param handwritingRegion new stylus handwritable {@link Region} that can accept stylus touch.
+ */
+ @FlaggedApi(Flags.FLAG_ADAPTIVE_HANDWRITING_BOUNDS)
+ public final void setStylusHandwritingRegion(@NonNull Region handwritingRegion) {
+ if (handwritingRegion.equals(mLastHandwritingRegion)) {
+ Log.v(TAG, "Failed to set setStylusHandwritingRegion():"
+ + " same region set twice.");
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Setting new handwriting region for stylus handwriting "
+ + handwritingRegion + " from last " + mLastHandwritingRegion);
+ }
+ mLastHandwritingRegion = handwritingRegion;
+ }
/**
* Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 036ccd84a600..4c9f08d80d4b 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -123,11 +123,6 @@ public final class MessageQueue {
// We can lift this restriction in the future after we've made it possible for test authors
// to test Looper and MessageQueue without resorting to reflection.
- // Holdback study.
- if (mUseConcurrent && Flags.messageQueueForceLegacy()) {
- mUseConcurrent = false;
- }
-
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index 224b10d0eaca..2b0042d653b1 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import com.android.internal.util.Preconditions;
@@ -106,7 +107,9 @@ public final class PerformanceHintManager {
* All timings should be in {@link SystemClock#uptimeNanos()}.
*/
public static class Session implements Closeable {
- private long mNativeSessionPtr;
+ /** @hide */
+ @UnsupportedAppUsage
+ public long mNativeSessionPtr;
/** @hide */
public Session(long nativeSessionPtr) {
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 9b1bf057b815..2ef8764a5221 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -4,15 +4,6 @@ container: "system"
# keep-sorted start block=yes newline_separated=yes
flag {
- # Holdback study for concurrent MessageQueue.
- # Do not promote beyond trunkfood.
- namespace: "system_performance"
- name: "message_queue_force_legacy"
- description: "Whether to holdback concurrent MessageQueue (force legacy)."
- bug: "336880969"
-}
-
-flag {
name: "adpf_gpu_report_actual_work_duration"
is_exported: true
namespace: "game"
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index b5a822b715d5..1093503a45a5 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -418,3 +418,12 @@ flag {
description: "Add new checkOp APIs that accept attributionTag"
bug: "240617242"
}
+
+flag {
+ name: "device_policy_management_role_split_create_managed_profile_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "enterprise"
+ description: "Gives the device policy management role the ability to create a managed profile using new APIs"
+ bug: "375382324"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a35c9c1cd4ec..19b0c6fcdd77 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -20454,6 +20454,29 @@ public final class Settings {
* @hide
*/
public static final String AUTO_BEDTIME_MODE = "auto_bedtime_mode";
+
+ /**
+ * Indicates that all elements of the system status tray on wear should be rendered
+ * by default wear system.
+ *
+ * @hide
+ */
+ public static final int STATUS_TRAY_CONFIGURATION_DEFAULT = 0;
+
+ /**
+ * Indicates that all elements of the system status tray on wear should be hidden.
+ *
+ * @hide
+ */
+ public static final int STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN = 1;
+
+ /**
+ * Configuration of system status tray in wear.
+ *
+ * @hide
+ */
+ public static final String WEAR_SYSTEM_STATUS_TRAY_CONFIGURATION =
+ "wear_system_status_tray_configuration";
}
}
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
index 61213e6293ba..a825a7e110f5 100644
--- a/core/java/android/service/carrier/CarrierMessagingService.java
+++ b/core/java/android/service/carrier/CarrierMessagingService.java
@@ -16,6 +16,7 @@
package android.service.carrier;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +27,8 @@ import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -97,16 +100,317 @@ public abstract class CarrierMessagingService extends Service {
public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
/**
- * SMS/MMS sending failed. We should not retry via the carrier network.
+ * SMS/MMS sending failed due to an unspecified issue. Sending will not be retried via the
+ * carrier network.
+ *
+ * <p>Maps to SmsManager.RESULT_RIL_GENERIC_FAILURE for SMS and SmsManager.MMS_ERROR_UNSPECIFIED
+ * for MMS.
*/
public static final int SEND_STATUS_ERROR = 2;
+ /**
+ * More precise error reasons for outbound SMS send requests. These will not be retried on the
+ * carrier network.
+ *
+ * <p>Each code maps directly to an SmsManager code (e.g. SEND_STATS_RESULT_ERROR_NULL_PDU maps
+ * to SmsManager.RESULT_ERROR_NULL_PDU).
+ */
+
+ /**
+ * Generic failure cause.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE = 200;
+
+ /**
+ * Failed because no pdu provided.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_NULL_PDU
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_NULL_PDU = 201;
+
+ /**
+ * Failed because service is currently unavailable.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_NO_SERVICE = 202;
+
+ /**
+ * Failed because we reached the sending queue limit.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED = 203;
+
+ /**
+ * Failed because FDN is enabled.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE = 204;
+
+ /**
+ * Failed because user denied the sending of this short code.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 205;
+
+ /**
+ * Failed because the user has denied this app ever send premium short codes.
+ *
+ * @see android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 206;
+
+ /**
+ * Failed because of network rejection.
+ *
+ * @see android.telephony.SmsManager.RESULT_NETWORK_REJECT
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_NETWORK_REJECT = 207;
+
+ /**
+ * Failed because of invalid arguments.
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_ARGUMENTS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_ARGUMENTS = 208;
+
+ /**
+ * Failed because of an invalid state.
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_STATE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_STATE = 209;
+
+ /**
+ * Failed because the sms format is not valid.
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_SMS_FORMAT
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_SMS_FORMAT = 210;
+
+ /**
+ * Failed because of a network error.
+ *
+ * @see android.telephony.SmsManager.RESULT_NETWORK_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_NETWORK_ERROR = 211;
+
+ /**
+ * Failed because of an encoding error.
+ *
+ * @see android.telephony.SmsManager.RESULT_ENCODING_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_ENCODING_ERROR = 212;
+
+ /**
+ * Failed because of an invalid smsc address
+ *
+ * @see android.telephony.SmsManager.RESULT_INVALID_SMSC_ADDRESS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS = 213;
+
+ /**
+ * Failed because the operation is not allowed.
+ *
+ * @see android.telephony.SmsManager.RESULT_OPERATION_NOT_ALLOWED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED = 214;
+
+ /**
+ * Failed because the operation was cancelled.
+ *
+ * @see android.telephony.SmsManager.RESULT_CANCELLED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_CANCELLED = 215;
+
+ /**
+ * Failed because the request is not supported.
+ *
+ * @see android.telephony.SmsManager.RESULT_REQUEST_NOT_SUPPORTED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED = 216;
+
+ /**
+ * Failed sending during an emergency call.
+ *
+ * @see android.telephony.SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY = 217;
+
+ /**
+ * Failed to send an sms retry.
+ *
+ * @see android.telephony.SmsManager.RESULT_SMS_SEND_RETRY_FAILED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED = 218;
+
+ /**
+ * More precise error reasons for outbound MMS send requests. These will not be retried on the
+ * carrier network.
+ *
+ * <p>Each code maps directly to an SmsManager code (e.g.
+ * SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS maps to SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS).
+ */
+
+ /**
+ * Unspecific MMS error occurred during send.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNSPECIFIED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_UNSPECIFIED = 400;
+
+ /**
+ * ApnException occurred during MMS network setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_APN
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_INVALID_APN = 401;
+
+ /**
+ * An error occurred during the MMS connection setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 402;
+
+ /**
+ * An error occurred during the HTTP client setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_HTTP_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_HTTP_FAILURE = 403;
+
+ /**
+ * An I/O error occurred reading the PDU.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_IO_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_IO_ERROR = 404;
+
+ /**
+ * An error occurred while retrying sending the MMS.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_RETRY
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_RETRY = 405;
+
+ /**
+ * The carrier-dependent configuration values could not be loaded.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_CONFIGURATION_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 406;
+
+ /**
+ * There is neither Wi-Fi nor mobile data network.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_NO_DATA_NETWORK
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK = 407;
+
+ /**
+ * The subscription id for the send is invalid.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 408;
+
+ /**
+ * The subscription id for the send is inactive.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 409;
+
+ /**
+ * Data is disabled for the MMS APN.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_DATA_DISABLED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_DATA_DISABLED = 410;
+
+ /**
+ * MMS is disabled by a carrier.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 411;
+
/** @hide */
- @IntDef(prefix = { "SEND_STATUS_" }, value = {
- SEND_STATUS_OK,
- SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- SEND_STATUS_ERROR
- })
+ @IntDef(
+ prefix = {"SEND_STATUS_"},
+ value = {
+ SEND_STATUS_OK,
+ SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
+ SEND_STATUS_ERROR,
+ SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE,
+ SEND_STATUS_RESULT_ERROR_NULL_PDU,
+ SEND_STATUS_RESULT_ERROR_NO_SERVICE,
+ SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED,
+ SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE,
+ SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED,
+ SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED,
+ SEND_STATUS_RESULT_NETWORK_REJECT,
+ SEND_STATUS_RESULT_INVALID_ARGUMENTS,
+ SEND_STATUS_RESULT_INVALID_STATE,
+ SEND_STATUS_RESULT_INVALID_SMS_FORMAT,
+ SEND_STATUS_RESULT_NETWORK_ERROR,
+ SEND_STATUS_RESULT_ENCODING_ERROR,
+ SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS,
+ SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED,
+ SEND_STATUS_RESULT_CANCELLED,
+ SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED,
+ SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY,
+ SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED,
+ SEND_STATUS_MMS_ERROR_UNSPECIFIED,
+ SEND_STATUS_MMS_ERROR_INVALID_APN,
+ SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS,
+ SEND_STATUS_MMS_ERROR_HTTP_FAILURE,
+ SEND_STATUS_MMS_ERROR_IO_ERROR,
+ SEND_STATUS_MMS_ERROR_RETRY,
+ SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR,
+ SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK,
+ SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID,
+ SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION,
+ SEND_STATUS_MMS_ERROR_DATA_DISABLED,
+ SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface SendResult {}
@@ -121,16 +425,138 @@ public abstract class CarrierMessagingService extends Service {
public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
/**
- * MMS downloading failed. We should not retry via the carrier network.
+ * MMS downloading failed due to an unspecified issue. Downloading will not be retried via the
+ * carrier network.
+ *
+ * <p>Maps to SmsManager.MMR_ERROR_UNSPECIFIED.
*/
public static final int DOWNLOAD_STATUS_ERROR = 2;
+ /**
+ * More precise error reasons for inbound MMS download requests. These will not be retried on
+ * the carrier network.
+ *
+ * <p>Each code maps directly to an SmsManager code (e.g.
+ * DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS maps to
+ * SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS).
+ */
+
+ /**
+ * Unspecific MMS error occurred during download.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNSPECIFIED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED = 600;
+
+ /**
+ * ApnException occurred during MMS network setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_APN
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN = 601;
+
+ /**
+ * An error occurred during the MMS connection setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 602;
+
+ /**
+ * An error occurred during the HTTP client setup.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_HTTP_FAILURE
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE = 603;
+
+ /**
+ * An I/O error occurred reading the PDU.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_IO_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR = 604;
+
+ /**
+ * An error occurred while retrying downloading the MMS.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_RETRY
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_RETRY = 605;
+
+ /**
+ * The carrier-dependent configuration values could not be loaded.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_CONFIGURATION_ERROR
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 606;
+
+ /**
+ * There is neither Wi-Fi nor mobile data network.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_NO_DATA_NETWORK
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK = 607;
+
+ /**
+ * The subscription id for the download is invalid.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 608;
+
+ /**
+ * The subscription id for the download is inactive.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 609;
+
+ /**
+ * Data is disabled for the MMS APN.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_DATA_DISABLED
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED = 610;
+
+ /**
+ * MMS is disabled by a carrier.
+ *
+ * @see android.telephony.SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ */
+ @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE)
+ public static final int DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 611;
+
/** @hide */
- @IntDef(prefix = { "DOWNLOAD_STATUS_" }, value = {
- DOWNLOAD_STATUS_OK,
- DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK,
- DOWNLOAD_STATUS_ERROR
- })
+ @IntDef(
+ prefix = {"DOWNLOAD_STATUS_"},
+ value = {
+ DOWNLOAD_STATUS_OK,
+ DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK,
+ DOWNLOAD_STATUS_ERROR,
+ DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED,
+ DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN,
+ DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS,
+ DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE,
+ DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR,
+ DOWNLOAD_STATUS_MMS_ERROR_RETRY,
+ DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR,
+ DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK,
+ DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID,
+ DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION,
+ DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED,
+ DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DownloadResult {}
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index aebe7ea7ee61..0f78c9e93a00 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -88,9 +88,10 @@ void ensureAPerformanceHintBindingInitialized() {
"Failed to find required symbol "
"APerformanceHint_getPreferredUpdateRateNanos!");
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ gAPH_createSessionFn =
+ (APH_createSession)dlsym(handle_, "APerformanceHint_createSessionFromJava");
LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
+ "Failed to find required symbol APerformanceHint_createSessionFromJava!");
gAPH_updateTargetWorkDurationFn =
(APH_updateTargetWorkDuration)dlsym(handle_,
@@ -106,9 +107,9 @@ void ensureAPerformanceHintBindingInitialized() {
"Failed to find required symbol "
"APerformanceHint_reportActualWorkDuration!");
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSessionFromJava");
LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
+ "Failed to find required symbol APerformanceHint_closeSessionFromJava!");
gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 26e63bc092fa..8042b30df4dc 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -171,6 +171,7 @@ android_app {
"android.os.vibrator.flags-aconfig",
"android.media.tv.flags-aconfig",
"android.security.flags-aconfig",
+ "device_policy_aconfig_flags",
"com.android.hardware.input.input-aconfig",
"aconfig_trade_in_mode_flags",
"art-aconfig-flags",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8cc7b0b8f942..9b2d3384d99c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2649,6 +2649,22 @@
android:label="@string/permlab_getAccounts" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+ <!-- @SystemApi Allows access to remove an account.
+ @FlaggedApi(android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.REMOVE_ACCOUNTS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" />
+
+ <!-- @SystemApi Allows access to copy an account to another user.
+ @FlaggedApi(android.app.admin.flags.Flags.FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.COPY_ACCOUNTS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.app.admin.flags.split_create_managed_profile_enabled" />
+
<!-- Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 75aca1b8820c..7ce2ed823540 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -23,10 +23,11 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.platform.test.ravenwood.RavenwoodConfig;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Objects;
@@ -39,8 +40,8 @@ public class SystemPropertiesTest {
private static final String PERSIST_KEY = "persist.sys.testkey";
private static final String NONEXIST_KEY = "doesnotexist_2341431";
- @RavenwoodConfig.Config
- public static final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder()
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
.setSystemPropertyMutable(KEY, null)
.setSystemPropertyMutable(UNSET_KEY, null)
.setSystemPropertyMutable(PERSIST_KEY, null)
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 5e41865cd31e..375968ab0ad2 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -113,7 +113,7 @@
style="?android:attr/buttonBarButtonStyle"
android:layout_width="41dp"
android:layout_height="@dimen/desktop_mode_maximize_menu_button_height"
- android:layout_marginRight="4dp"
+ android:layout_marginEnd="4dp"
android:background="@drawable/desktop_mode_maximize_menu_button_background"
android:importantForAccessibility="yes"
android:contentDescription="@string/desktop_mode_maximize_menu_snap_left_button_text"
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7078d66d265c..21ec84d9bc0a 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -504,17 +504,15 @@
<dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
<!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
<dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
- <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
- <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom">8dp</dimen>
<!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
<dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
<!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
- <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">15dp</dimen>
<!-- The padding between the outline and fill of the maximize menu immersive button. -->
- <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">4dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">0dp</dimen>
<!-- The corner radius of the maximize menu. -->
- <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_corner_radius">16dp</dimen>
<!-- The radius of the Maximize menu shadow. -->
<dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 012579a6d40c..468c345259d0 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -318,7 +318,7 @@
<!-- Maximize menu maximize button string. -->
<string name="desktop_mode_maximize_menu_maximize_text">Maximize Screen</string>
<!-- Maximize menu snap buttons string. -->
- <string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
+ <string name="desktop_mode_maximize_menu_snap_text">Resize</string>
<!-- Snap resizing non-resizable string. -->
<string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
<!-- Accessibility text for the Maximize Menu's immersive button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 603a9ec6bedb..b82496e45415 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1327,7 +1327,11 @@ public class BubbleController implements ConfigurationChangeListener,
/** Promote the provided bubble from the overflow view. */
public void promoteBubbleFromOverflow(Bubble bubble) {
- mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
+ if (isShowingAsBubbleBar()) {
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR);
+ } else {
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
+ }
ProtoLog.d(WM_SHELL_BUBBLES, "promoteBubbleFromOverflow=%s", bubble.getKey());
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.setShouldAutoExpand(true);
@@ -1350,11 +1354,7 @@ public class BubbleController implements ConfigurationChangeListener,
if (BubbleOverflow.KEY.equals(key)) {
mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
mLayerView.showExpandedView(mBubbleData.getOverflow());
- if (wasExpanded) {
- mLogger.log(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
- } else {
- mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
- }
+ mLogger.log(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_SELECTED);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 4de9dfa54c5d..294569190f68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -759,7 +759,9 @@ public class BubbleData {
if (b != null) {
b.stopInflation();
}
- mLogger.logOverflowRemove(b, reason);
+ if (!mPositioner.isShowingInBubbleBar()) {
+ mLogger.logStackOverflowRemove(b, reason);
+ }
mOverflowBubbles.remove(b);
mStateChange.bubbleRemoved(b, reason);
mStateChange.removedOverflowBubble = b;
@@ -802,6 +804,27 @@ public class BubbleData {
setNewSelectedIndex(indexToRemove);
}
maybeSendDeleteIntent(reason, bubbleToRemove);
+
+ if (mPositioner.isShowingInBubbleBar()) {
+ logBubbleBarBubbleRemoved(bubbleToRemove, reason);
+ }
+ }
+
+ private void logBubbleBarBubbleRemoved(Bubble bubble, @DismissReason int reason) {
+ switch (reason) {
+ case Bubbles.DISMISS_NOTIF_CANCEL:
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_CANCELED);
+ break;
+ case Bubbles.DISMISS_TASK_FINISHED:
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH);
+ break;
+ case Bubbles.DISMISS_BLOCKED:
+ case Bubbles.DISMISS_NO_LONGER_BUBBLE:
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED);
+ break;
+ default:
+ // skip logging other events
+ }
}
private void setNewSelectedIndex(int indexOfSelected) {
@@ -862,7 +885,7 @@ public class BubbleData {
return;
}
ProtoLog.d(WM_SHELL_BUBBLES, "overflowBubble=%s", bubble.getKey());
- mLogger.logOverflowAdd(bubble, reason);
+ mLogger.logOverflowAdd(bubble, mPositioner.isShowingInBubbleBar(), reason);
if (mOverflowBubbles.isEmpty()) {
mStateChange.showOverflowChanged = true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 36630733e1da..347df330c4b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -182,10 +182,12 @@ public class BubbleLogger {
}
/**
+ * Log when a bubble is removed from overflow in stack view
+ *
* @param b Bubble removed from overflow
* @param r Reason that bubble was removed
*/
- public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) {
+ public void logStackOverflowRemove(Bubble b, @Bubbles.DismissReason int r) {
if (r == Bubbles.DISMISS_NOTIF_CANCEL) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
} else if (r == Bubbles.DISMISS_GROUP_CANCELLED) {
@@ -201,13 +203,19 @@ public class BubbleLogger {
* @param b Bubble added to overflow
* @param r Reason that bubble was added to overflow
*/
- public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) {
- if (r == Bubbles.DISMISS_AGED) {
- log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
- } else if (r == Bubbles.DISMISS_USER_GESTURE) {
- log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
- } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
- log(b, Event.BUBBLE_OVERFLOW_RECOVER);
+ public void logOverflowAdd(Bubble b, boolean bubbleBar, @Bubbles.DismissReason int r) {
+ if (bubbleBar) {
+ if (r == Bubbles.DISMISS_AGED) {
+ log(b, Event.BUBBLE_BAR_OVERFLOW_ADD_AGED);
+ }
+ } else {
+ if (r == Bubbles.DISMISS_AGED) {
+ log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
+ } else if (r == Bubbles.DISMISS_USER_GESTURE) {
+ log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+ } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
+ log(b, Event.BUBBLE_OVERFLOW_RECOVER);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c386c9398624..068b2d246500 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -830,6 +830,13 @@ public class BubblePositioner {
mShowingInBubbleBar = showingInBubbleBar;
}
+ /**
+ * Whether bubbles ar showing in the bubble bar from launcher.
+ */
+ boolean isShowingInBubbleBar() {
+ return mShowingInBubbleBar;
+ }
+
public void setBubbleBarLocation(BubbleBarLocation location) {
mBubbleBarLocation = location;
}
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 6fc6eb783a17..a0bdd9fad510 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
@@ -1668,6 +1668,13 @@ class DesktopTasksController(
transition, wct, task.displayId
)
}
+ } else if (taskRepository.isActiveTask(task.taskId)) {
+ // If a freeform task receives a request for a fullscreen launch, apply the same
+ // changes we do for similar transitions. The task not having WINDOWING_MODE_UNDEFINED
+ // set when needed can interfere with future split / multi-instance transitions.
+ return WindowContainerTransaction().also { wct ->
+ addMoveToFullscreenChanges(wct, task)
+ }
}
return null
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 319bfac734ba..e4f83333edbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -173,6 +173,10 @@ public class KeyguardTransitionHandler
return mKeyguardShowing;
}
+ public boolean isKeyguardAnimating() {
+ return !mStartedTransitions.isEmpty();
+ }
+
@Override
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 17483dd68632..92d1f9c26bbc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -370,7 +370,8 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
if (mRecentsHandler != null) {
if (mSplitHandler.isSplitScreenVisible()) {
return this::setRecentsTransitionDuringSplit;
- } else if (mKeyguardHandler.isKeyguardShowing()) {
+ } else if (mKeyguardHandler.isKeyguardShowing()
+ && !mKeyguardHandler.isKeyguardAnimating()) {
return this::setRecentsTransitionDuringKeyguard;
} else if (mDesktopTasksController != null
// Check on the default display. Recents/gesture nav is only available there
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index fd4d568326d0..8cdbe26a2c76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -117,6 +117,11 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for Recents during"
+ " Keyguard #%d", info.getDebugId());
+ if (!mKeyguardHandler.isKeyguardShowing() || mKeyguardHandler.isKeyguardAnimating()) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Cancel mixed transition because "
+ + "keyguard state was changed #%d", info.getDebugId());
+ return false;
+ }
if (mInfo == null) {
mInfo = info;
mFinishT = finishTransaction;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 4bb1e7b6cc05..11a7cf8da8d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -70,6 +70,7 @@ import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHo
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.OPACITY_12
import com.android.wm.shell.windowdecor.common.OPACITY_40
+import com.android.wm.shell.windowdecor.common.OPACITY_60
import com.android.wm.shell.windowdecor.common.withAlpha
import java.util.function.Supplier
@@ -310,8 +311,6 @@ class MaximizeMenu(
.desktop_mode_maximize_menu_immersive_button_fill_padding)
private val maximizeFillPaddingDefault = context.resources.getDimensionPixelSize(R.dimen
.desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
- private val maximizeFillPaddingBottom = context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom)
private val maximizeRestoreFillPaddingVertical = context.resources.getDimensionPixelSize(
R.dimen.desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
private val maximizeRestoreFillPaddingHorizontal = context.resources.getDimensionPixelSize(
@@ -320,7 +319,7 @@ class MaximizeMenu(
maximizeFillPaddingDefault,
maximizeFillPaddingDefault,
maximizeFillPaddingDefault,
- maximizeFillPaddingBottom
+ maximizeFillPaddingDefault
)
private val maximizeRestoreFillPaddingRect = Rect(
maximizeRestoreFillPaddingHorizontal,
@@ -684,7 +683,7 @@ class MaximizeMenu(
inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(),
semiActiveSnapSideColor = colorScheme.primary.toArgb().withAlpha(OPACITY_40),
activeSnapSideColor = colorScheme.primary.toArgb(),
- inactiveStrokeColor = colorScheme.outlineVariant.toArgb(),
+ inactiveStrokeColor = colorScheme.outlineVariant.toArgb().withAlpha(OPACITY_60),
activeStrokeColor = colorScheme.primary.toArgb(),
inactiveBackgroundColor = menuBackgroundColor,
activeBackgroundColor = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
@@ -753,7 +752,8 @@ class MaximizeMenu(
val activeStrokeAndFill = colorScheme.primary.toArgb()
val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
val activeDrawable = createMaximizeOrImmersiveButtonDrawable(
- strokeAndFillColor = activeStrokeAndFill,
+ strokeColor = activeStrokeAndFill,
+ fillColor = activeStrokeAndFill,
backgroundColor = activeBackground,
// Add a mask with the menu background's color because the active background color is
// semi transparent, otherwise the transparency will reveal the stroke/fill color
@@ -770,7 +770,8 @@ class MaximizeMenu(
addState(
StateSet.WILD_CARD,
createMaximizeOrImmersiveButtonDrawable(
- strokeAndFillColor = colorScheme.outlineVariant.toArgb(),
+ strokeColor = colorScheme.outlineVariant.toArgb().withAlpha(OPACITY_60),
+ fillColor = colorScheme.outlineVariant.toArgb(),
backgroundColor = colorScheme.surfaceContainerLow.toArgb(),
backgroundMask = null, // not needed because the bg color is fully opaque
fillPadding = fillPadding,
@@ -780,7 +781,8 @@ class MaximizeMenu(
}
private fun createMaximizeOrImmersiveButtonDrawable(
- @ColorInt strokeAndFillColor: Int,
+ @ColorInt strokeColor: Int,
+ @ColorInt fillColor: Int,
@ColorInt backgroundColor: Int,
@ColorInt backgroundMask: Int?,
fillPadding: Rect,
@@ -794,7 +796,7 @@ class MaximizeMenu(
null /* inset */,
null /* innerRadii */
)
- paint.color = strokeAndFillColor
+ paint.color = strokeColor
paint.style = Paint.Style.FILL
})
// Second layer, a mask for the next (background) layer if needed because of
@@ -829,7 +831,7 @@ class MaximizeMenu(
null /* inset */,
null /* innerRadii */
)
- paint.color = strokeAndFillColor
+ paint.color = fillColor
paint.style = Paint.Style.FILL
})
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
index f7cfbfa88485..c5057aa3cc18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
@@ -52,6 +52,7 @@ const val OPACITY_12 = 31
const val OPACITY_15 = 38
const val OPACITY_40 = 102
const val OPACITY_55 = 140
+const val OPACITY_60 = 153
const val OPACITY_65 = 166
/**
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index 65e50f86e8fe..19829e7e5677 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -6,6 +6,7 @@ pablogamito@google.com
lbill@google.com
madym@google.com
hwwang@google.com
+gabiyev@google.com
chenghsiuchang@google.com
atsjenk@google.com
jorgegil@google.com
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp
deleted file mode 100644
index 85e6a8d1d865..000000000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/Android.bp
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// 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
-//
-// 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "WMShellFlickerTestsMediaProjection",
- defaults: ["WMShellFlickerTestsDefault"],
- manifest: "AndroidManifest.xml",
- test_config_template: "AndroidTestTemplate.xml",
- srcs: ["src/**/*.kt"],
- static_libs: [
- "WMShellFlickerTestsBase",
- "WMShellScenariosMediaProjection",
- "WMShellTestUtils",
- ],
- data: ["trace_config/*"],
-}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml
deleted file mode 100644
index 74b0daf3a2aa..000000000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidManifest.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker">
-
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
- <!-- Read and write traces from external storage -->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <!-- Allow the test to write directly to /sdcard/ -->
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- <!-- Write secure settings -->
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <!-- Capture screen contents -->
- <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
- <!-- Enable / Disable tracing !-->
- <uses-permission android:name="android.permission.DUMP" />
- <!-- Run layers trace -->
- <uses-permission android:name="android.permission.HARDWARE_TEST"/>
- <!-- Capture screen recording -->
- <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
- <!-- Workaround grant runtime permission exception from b/152733071 -->
- <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
- <uses-permission android:name="android.permission.READ_LOGS"/>
- <!-- Force-stop test apps -->
- <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
- <!-- Control test app's media session -->
- <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <!-- ATM.removeRootTasksWithActivityTypes() -->
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
- <!-- Enable bubble notification-->
- <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
- <!-- Allow the test to connect to perfetto trace processor -->
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
- <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
-
- <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
- <application android:requestLegacyExternalStorage="true"
- android:networkSecurityConfig="@xml/network_security_config"
- android:largeHeap="true">
- <uses-library android:name="android.test.runner"/>
-
- <service android:name=".NotificationListener"
- android:exported="true"
- android:label="WMShellTestsNotificationListenerService"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
- <intent-filter>
- <action android:name="android.service.notification.NotificationListenerService" />
- </intent-filter>
- </service>
-
- <service android:name="com.android.wm.shell.flicker.utils.MediaProjectionService"
- android:foregroundServiceType="mediaProjection"
- android:label="WMShellTestsMediaProjectionService"
- android:enabled="true">
- </service>
-
- <!-- (b/197936012) Remove startup provider due to test timeout issue -->
- <provider
- android:name="androidx.startup.InitializationProvider"
- android:authorities="${applicationId}.androidx-startup"
- tools:node="remove" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker"
- android:label="WindowManager Shell Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml
deleted file mode 100644
index 40dbbac32c7f..000000000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/AndroidTestTemplate.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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.
- -->
-<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
- <option name="test-tag" value="FlickerTests"/>
- <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
- <option name="isolated-storage" value="false"/>
-
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- disable DeprecatedTargetSdk warning -->
- <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on"/>
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true"/>
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all"/>
- <!-- set WM tracing to frame (avoid incomplete states) -->
- <option name="run-command" value="cmd window tracing frame"/>
- <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
- <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
- <!-- ensure lock screen mode is swipe -->
- <option name="run-command" value="locksettings set-disabled false"/>
- <!-- restart launcher to activate TAPL -->
- <option name="run-command"
- value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
- <!-- Increase trace size: 20mb for WM and 80mb for SF -->
- <option name="run-command" value="cmd window tracing size 20480"/>
- <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="test-user-token" value="%TEST_USER%"/>
- <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
- <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
- <option name="run-command" value="settings put system show_touches 1"/>
- <option name="run-command" value="settings put system pointer_location 1"/>
- <option name="teardown-command"
- value="settings delete secure show_ime_with_hard_keyboard"/>
- <option name="teardown-command" value="settings delete system show_touches"/>
- <option name="teardown-command" value="settings delete system pointer_location"/>
- <option name="teardown-command"
- value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="{MODULE}.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk"/>
- </target_preparer>
-
- <!-- Needed for pushing the trace config file -->
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="push-file"
- key="trace_config.textproto"
- value="/data/misc/perfetto-traces/trace_config.textproto"
- />
- <!--Install the content provider automatically when we push some file in sdcard folder.-->
- <!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="{PACKAGE}"/>
- <option name="shell-timeout" value="6600s"/>
- <option name="test-timeout" value="6000s"/>
- <option name="hidden-api-checks" value="false"/>
- <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
- <!-- PerfettoListener related arguments -->
- <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
- <option name="instrumentation-arg"
- key="perfetto_config_file"
- value="trace_config.textproto"
- />
- <option name="instrumentation-arg" key="per_run" value="true"/>
- <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
- </test>
- <!-- Needed for pulling the collected trace config on to the host -->
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="pull-pattern-keys" value="perfetto_file_path"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="collect-on-run-ended-only" value="true"/>
- <option name="clean-up" value="true"/>
- </metrics_collector>
-</configuration>
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto
deleted file mode 100644
index 9f2e49755fec..000000000000
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/trace_config/trace_config.textproto
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (C) 2023 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.
-
-# proto-message: TraceConfig
-
-# Enable periodic flushing of the trace buffer into the output file.
-write_into_file: true
-
-# Writes the userspace buffer into the file every 1s.
-file_write_period_ms: 2500
-
-# See b/126487238 - we need to guarantee ordering of events.
-flush_period_ms: 30000
-
-# The trace buffers needs to be big enough to hold |file_write_period_ms| of
-# trace data. The trace buffer sizing depends on the number of trace categories
-# enabled and the device activity.
-
-# RSS events
-buffers: {
- size_kb: 63488
- fill_policy: RING_BUFFER
-}
-
-data_sources {
- config {
- name: "linux.process_stats"
- target_buffer: 0
- # polled per-process memory counters and process/thread names.
- # If you don't want the polled counters, remove the "process_stats_config"
- # section, but keep the data source itself as it still provides on-demand
- # thread/process naming for ftrace data below.
- process_stats_config {
- scan_all_processes_on_start: true
- }
- }
-}
-
-data_sources: {
- config {
- name: "linux.ftrace"
- ftrace_config {
- ftrace_events: "ftrace/print"
- ftrace_events: "task/task_newtask"
- ftrace_events: "task/task_rename"
- atrace_categories: "ss"
- atrace_categories: "wm"
- atrace_categories: "am"
- atrace_categories: "aidl"
- atrace_categories: "input"
- atrace_categories: "binder_driver"
- atrace_categories: "sched_process_exit"
- atrace_apps: "com.android.server.wm.flicker.testapp"
- atrace_apps: "com.android.systemui"
- atrace_apps: "com.android.wm.shell.flicker.service"
- atrace_apps: "com.google.android.apps.nexuslauncher"
- }
- }
-}
-
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt
new file mode 100644
index 000000000000..2b9772d9cbdd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionFromSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjectionFromSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjectionFromSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionFromSplitScreenTest() : StartAppMediaProjectionFromSplitScreen() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt
new file mode 100644
index 000000000000..e92297b48166
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionInSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjectionInSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjectionInSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionInSplitScreenTest() : StartAppMediaProjectionInSplitScreen() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt
new file mode 100644
index 000000000000..3f8107592667
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjection
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjection]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionTest() : StartAppMediaProjection() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt
new file mode 100644
index 000000000000..1975cc7f86d2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartAppMediaProjectionWithExtraIntentTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartAppMediaProjectionWithExtraIntent
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartAppMediaProjectionWithExtraIntent]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartAppMediaProjectionWithExtraIntentTest : StartAppMediaProjectionWithExtraIntent() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt
new file mode 100644
index 000000000000..943033c1819c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionFromSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartRecentAppMediaProjectionFromSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartRecentAppMediaProjectionFromSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartRecentAppMediaProjectionFromSplitScreenTest() : StartRecentAppMediaProjectionFromSplitScreen() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt
new file mode 100644
index 000000000000..6facfd5b0063
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionInSplitScreenTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartRecentAppMediaProjectionInSplitScreen
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartRecentAppMediaProjectionInSplitScreen]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartRecentAppMediaProjectionInSplitScreenTest() : StartRecentAppMediaProjectionInSplitScreen() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt
new file mode 100644
index 000000000000..bab09052715a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/functional/StartRecentAppMediaProjectionTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.StartRecentAppMediaProjection
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [StartRecentAppMediaProjection]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class StartRecentAppMediaProjectionTest() : StartRecentAppMediaProjection() \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt
new file mode 100644
index 000000000000..fe2c57801f72
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjection.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session.
+ *
+ * This is for testing that the requested app is opened as expected upon selecting it from the app
+ * selector, so capture can proceed as expected.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjection {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ testApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjection(wmHelper, targetApp)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt
new file mode 100644
index 000000000000..3beece8c38b8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionFromSplitScreen.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session, while the HOST app is in
+ * split screen
+ *
+ * This is for testing that the requested app is opened as expected upon selecting it from the app
+ * selector, so capture can proceed as expected.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjectionFromSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val simpleApp = SimpleAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, simpleApp, testApp, initialRotation)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, simpleApp, testApp)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjection(wmHelper, targetApp)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt
new file mode 100644
index 000000000000..d3186ae88081
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionInSplitScreen.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session, while the TARGET app is in
+ * split screen (next to the host app)
+ *
+ * This is for testing that the split pair isn't broken, and capture still proceeds as expected
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjectionInSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, targetApp, testApp, initialRotation)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjection(wmHelper, targetApp)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ targetApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt
new file mode 100644
index 000000000000..0b2a1ca23cdb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithExtraIntent.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session but launches an intent to
+ * return to the home screen, before the intent for opening the requested app to capture.
+ *
+ * This is for testing that even if a different intent interrupts the process the launching the
+ * requested capture target, the MediaProjection process isn't interrupted and the device is still
+ * interactive.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartAppMediaProjectionWithExtraIntent {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ testApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjectionWithExtraIntent(wmHelper, targetApp)
+
+ // Check we can still interact with device after
+ tapl.workspace.switchToAllApps().getAppIcon(targetApp.appName).launch(targetApp.packageName)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt
new file mode 100644
index 000000000000..30e0e4aa490a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjection.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session from recents.
+ *
+ * This is for testing that the app is started from recents and capture proceeds as expected.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartRecentAppMediaProjection {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ targetApp.open()
+ testApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjectionFromRecents(wmHelper, targetApp)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt
new file mode 100644
index 000000000000..f1dcf1fab140
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionFromSplitScreen.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session from recents while the HOST
+ * app is in split screen.
+ *
+ * This is for testing that the split pair isn't broken, and capture still proceeds as expected
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartRecentAppMediaProjectionFromSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val simpleApp = SimpleAppHelper(instrumentation)
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ targetApp.open()
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, simpleApp, testApp, initialRotation)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ // The app we want to open for PSS will be the second item in the carousel,
+ // because the first will be the app open in split screen alongside the MediaProjection app
+ testApp.startSingleAppMediaProjectionFromRecents(wmHelper, targetApp, recentTasksIndex = 1)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt
new file mode 100644
index 000000000000..0a6992f9a152
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartRecentAppMediaProjectionInSplitScreen.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.StartMediaProjectionAppHelper
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+ * Test scenario which requests an a single-app MediaProjection session from recents while the
+ * TARGET app is in split screen (with host app).
+ *
+ * This is for testing that the split pair isn't broken, and capture still proceeds as expected
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class StartRecentAppMediaProjectionInSplitScreen {
+
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+
+ private val initialRotation = Rotation.ROTATION_0
+ private val targetApp = CalculatorAppHelper(instrumentation)
+ private val testApp = StartMediaProjectionAppHelper(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, initialRotation)
+
+ @Before
+ fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(initialRotation.value)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, targetApp, testApp, initialRotation)
+ }
+
+ @Test
+ open fun startMediaProjection() {
+ testApp.startSingleAppMediaProjectionFromRecents(wmHelper, targetApp)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ targetApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt
index f9706969ff11..8c2bdad364fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/MediaProjectionUtils.kt
@@ -17,7 +17,11 @@
package com.android.wm.shell.flicker.utils
object MediaProjectionUtils {
- const val REQUEST_CODE: Int = 99
+ // Request code for the normal media projection request
+ const val REQUEST_CODE_NORMAL: Int = 11
+ // Request code for the media projection request which will include an extra intent to open
+ // home screen before starting requested app
+ const val REQUEST_CODE_EXTRA_INTENT: Int = 12
const val MSG_START_FOREGROUND_DONE: Int = 1
const val MSG_SERVICE_DESTROYED: Int = 2
const val EXTRA_MESSENGER: String = "messenger"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 6fa37885b724..ce640b5e5195 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -47,6 +47,7 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.android.wm.shell.common.ShellExecutor;
@@ -102,6 +103,7 @@ public class BubbleDataTest extends ShellTestCase {
private BubbleData mBubbleData;
private TestableBubblePositioner mPositioner;
+ private UiEventLoggerFake mUiEventLogger;
@Mock
private TimeSource mTimeSource;
@@ -112,8 +114,6 @@ public class BubbleDataTest extends ShellTestCase {
@Mock
private PendingIntent mDeleteIntent;
@Mock
- private BubbleLogger mBubbleLogger;
- @Mock
private BubbleEducationController mEducationController;
@Mock
private ShellExecutor mMainExecutor;
@@ -196,10 +196,12 @@ public class BubbleDataTest extends ShellTestCase {
mock(Icon.class),
mMainExecutor, mBgExecutor);
+ mUiEventLogger = new UiEventLoggerFake();
+
mPositioner = new TestableBubblePositioner(mContext,
mContext.getSystemService(WindowManager.class));
- mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
- mMainExecutor, mBgExecutor);
+ mBubbleData = new BubbleData(getContext(), new BubbleLogger(mUiEventLogger), mPositioner,
+ mEducationController, mMainExecutor, mBgExecutor);
// Used by BubbleData to set lastAccessedTime
when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -297,6 +299,82 @@ public class BubbleDataTest extends ShellTestCase {
}
@Test
+ public void testRemoveBubbleFromBubbleBar_notifCancelled_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_CANCELED.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_taskFinished_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_TASK_FINISHED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_notifBlocked_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_BLOCKED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_noLongerBubble_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId());
+ }
+
+ @Test
+ public void testRemoveBubbleFromBubbleBar_addToOverflow_logEvent() {
+ mPositioner.setShowingInBubbleBar(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_AGED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(1);
+ assertThat(mUiEventLogger.eventId(0)).isEqualTo(
+ BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_ADD_AGED.getId());
+ }
+
+ @Test
+ public void testRemoveBubble_notifCancelled_noLog() {
+ mPositioner.setShowingInBubbleBar(false);
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_BLOCKED);
+ assertThat(mUiEventLogger.numLogs()).isEqualTo(0);
+ }
+
+ @Test
public void ifSuppress_hideFlyout() {
// Setup
mBubbleData.setListener(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 5f58265b45f5..289fd2d838fd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -36,6 +36,8 @@ import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Rational;
@@ -46,6 +48,7 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.MockSurfaceControlHelper;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -67,6 +70,8 @@ import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
@@ -81,7 +86,13 @@ import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@DisableFlags(Flags.FLAG_ENABLE_PIP2)
public class PipTaskOrganizerTest extends ShellTestCase {
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
private PipTaskOrganizer mPipTaskOrganizer;
@Mock private DisplayController mMockDisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 96003515a485..b123f4dfac9e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -42,11 +42,14 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
@@ -74,6 +77,8 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -88,7 +93,11 @@ import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@DisableFlags(Flags.FLAG_ENABLE_PIP2)
public class PipControllerTest extends ShellTestCase {
+ @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
private PipController mPipController;
private ShellInit mShellInit;
private ShellController mShellController;
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index fb58a69747b3..b72e066e64ae 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -84,6 +84,7 @@ public:
// this value is only valid after the GPU has been initialized and there is a valid graphics
// context or if you are using the HWUI_NULL_GPU
int maxTextureSize() const;
+ bool hasMaxTextureSize() const { return mMaxTextureSize > 0; }
sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
SkColorType getWideColorType() {
static std::once_flag kFlag;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 2c23864317a4..4801bd1038a3 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -186,7 +186,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) ||
- CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
+ CC_UNLIKELY(properties().getWidth() <= 0) || CC_UNLIKELY(properties().getHeight() <= 0) ||
CC_UNLIKELY(!properties().fitsOnLayer())) {
if (CC_UNLIKELY(hasLayer())) {
this->setLayerSurface(nullptr);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index b1ad8b2eb1b9..4dc57004e401 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -545,7 +545,8 @@ public:
bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
- mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+ mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize() &&
+ mPrimitiveFields.mWidth > 0 && mPrimitiveFields.mHeight > 0;
}
bool promotedToLayer() const {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8ec04304a808..b36b8be10779 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -418,6 +418,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
RenderNode* target) {
mRenderThread.removeFrameCallback(this);
+ // Make sure we have a valid device info
+ if (!DeviceInfo::get()->hasMaxTextureSize()) {
+ (void)mRenderThread.requireGrContext();
+ }
+
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
const auto reason = wasSkipped(mCurrentFrameInfo);
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
index 3e8e0576bf49..6ec042cf23b0 100644
--- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -40,7 +40,11 @@ TEST(RenderProperties, layerValidity) {
props.setLeftTopRightBottom(0, 0, maxTextureSize + 1, maxTextureSize + 1);
ASSERT_FALSE(props.fitsOnLayer());
- // Too small, but still 'fits'. Not fitting is an error case, so don't report empty as such.
+ // Too small, we can't create a layer for a 0 width or height
props.setLeftTopRightBottom(0, 0, 100, 0);
- ASSERT_TRUE(props.fitsOnLayer());
+ ASSERT_FALSE(props.fitsOnLayer());
+
+ // Can't create a negative-sized layer
+ props.setLeftTopRightBottom(0, 0, -100, 300);
+ ASSERT_FALSE(props.fitsOnLayer());
}
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 4330f0addaa9..d993b8715260 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -28,15 +28,13 @@
#define INDENT " "
#define INDENT2 " "
-namespace android {
-
namespace {
-
// Time to spend fading out the pointer completely.
const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
-
} // namespace
+namespace android {
+
// --- MouseCursorController ---
MouseCursorController::MouseCursorController(PointerControllerContext& context)
@@ -66,21 +64,17 @@ MouseCursorController::~MouseCursorController() {
mLocked.pointerSprite.clear();
}
-vec2 MouseCursorController::move(float deltaX, float deltaY) {
+void MouseCursorController::move(float deltaX, float deltaY) {
#if DEBUG_MOUSE_CURSOR_UPDATES
ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
#endif
if (deltaX == 0.0f && deltaY == 0.0f) {
- return {};
+ return;
}
- // When transition occurs, the MouseCursorController object may or may not be deleted, depending
- // if there's another display on the other side of the transition. At this point we still need
- // to move the cursor to the boundary.
std::scoped_lock lock(mLock);
- const vec2 pos{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY};
- setPositionLocked(pos.x, pos.y);
- return vec2{pos.x - mLocked.pointerX, pos.y - mLocked.pointerY};
+
+ setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
}
void MouseCursorController::setPosition(float x, float y) {
@@ -88,26 +82,22 @@ void MouseCursorController::setPosition(float x, float y) {
ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
#endif
std::scoped_lock lock(mLock);
-
setPositionLocked(x, y);
}
-FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) {
+void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
+ const auto& v = mLocked.viewport;
+ if (!v.isValid()) return;
+
// The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside
// the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily
// close to the outside edge.
- return FloatRect{
+ const FloatRect bounds{
static_cast<float>(mLocked.viewport.logicalLeft),
static_cast<float>(mLocked.viewport.logicalTop),
static_cast<float>(mLocked.viewport.logicalRight - 1),
static_cast<float>(mLocked.viewport.logicalBottom - 1),
};
-}
-void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
- const auto& v = mLocked.viewport;
- if (!v.isValid()) return;
-
- const FloatRect bounds = getBoundsLocked();
mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x));
mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y));
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 10f0d7645105..12b31a8c531a 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -20,6 +20,9 @@
#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
#include <functional>
#include <map>
@@ -40,8 +43,7 @@ public:
MouseCursorController(PointerControllerContext& context);
~MouseCursorController();
- // Move the pointer and return unconsumed delta
- vec2 move(float deltaX, float deltaY);
+ void move(float deltaX, float deltaY);
void setPosition(float x, float y);
FloatPoint getPosition() const;
ui::LogicalDisplayId getDisplayId() const;
@@ -111,7 +113,6 @@ private:
bool doFadingAnimationLocked(nsecs_t timestamp);
void startAnimationLocked();
- FloatRect getBoundsLocked();
};
} // namespace android
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 59397dab592f..78d7d3a7051b 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -138,18 +138,15 @@ std::mutex& PointerController::getLock() const {
return mDisplayInfoListener->mLock;
}
-vec2 PointerController::move(float deltaX, float deltaY) {
+void PointerController::move(float deltaX, float deltaY) {
const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
- ui::Transform transform;
+ vec2 transformed;
{
std::scoped_lock lock(getLock());
- transform = getTransformForDisplayLocked(displayId);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
}
-
- vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
-
- vec2 unconsumedDelta = mCursorController.move(transformed.x, transformed.y);
- return transformWithoutTranslation(transform.inverse(), unconsumedDelta);
+ mCursorController.move(transformed.x, transformed.y);
}
void PointerController::setPosition(float x, float y) {
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 69bef546b788..ee8d1211341f 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -51,7 +51,7 @@ public:
~PointerController() override;
- vec2 move(float deltaX, float deltaY) override;
+ void move(float deltaX, float deltaY) override;
void setPosition(float x, float y) override;
FloatPoint getPosition() const override;
ui::LogicalDisplayId getDisplayId() const override;
@@ -165,7 +165,7 @@ public:
~TouchPointerController() override;
- vec2 move(float, float) override {
+ void move(float, float) override {
LOG_ALWAYS_FATAL("Should not be called");
}
void setPosition(float, float) override {
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 80c934a9bd95..5b00fca4d857 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -40,8 +40,6 @@ enum TestCursorType {
CURSOR_TYPE_CUSTOM = -1,
};
-static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
-
using ::testing::AllOf;
using ::testing::Field;
using ::testing::NiceMock;
@@ -401,135 +399,6 @@ INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest,
testing::Values(PointerControllerInterface::ControllerType::MOUSE,
PointerControllerInterface::ControllerType::STYLUS));
-class MousePointerControllerTest : public PointerControllerTest {
-protected:
- MousePointerControllerTest() {
- sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>);
- EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite));
-
- // create a mouse pointer controller
- mPointerController =
- PointerController::create(mPolicy, mLooper, *mSpriteController,
- PointerControllerInterface::ControllerType::MOUSE);
-
- // set display viewport
- DisplayViewport viewport;
- viewport.displayId = ui::LogicalDisplayId::DEFAULT;
- viewport.logicalRight = 5;
- viewport.logicalBottom = 5;
- viewport.physicalRight = 5;
- viewport.physicalBottom = 5;
- viewport.deviceWidth = 5;
- viewport.deviceHeight = 5;
- mPointerController->setDisplayViewport(viewport);
- }
-};
-
-struct MousePointerControllerTestParam {
- vec2 startPosition;
- vec2 delta;
- vec2 endPosition;
- vec2 unconsumedDelta;
-};
-
-class PointerControllerViewportTransitionTest
- : public MousePointerControllerTest,
- public testing::WithParamInterface<MousePointerControllerTestParam> {};
-
-TEST_P(PointerControllerViewportTransitionTest, testPointerViewportTransition) {
- const auto& params = GetParam();
-
- mPointerController->setPosition(params.startPosition.x, params.startPosition.y);
- auto unconsumedDelta = mPointerController->move(params.delta.x, params.delta.y);
-
- auto position = mPointerController->getPosition();
- EXPECT_NEAR(position.x, params.endPosition.x, EPSILON);
- EXPECT_NEAR(position.y, params.endPosition.y, EPSILON);
- EXPECT_NEAR(unconsumedDelta.x, params.unconsumedDelta.x, EPSILON);
- EXPECT_NEAR(unconsumedDelta.y, params.unconsumedDelta.y, EPSILON);
-}
-
-INSTANTIATE_TEST_SUITE_P(PointerControllerViewportTransitionTest,
- PointerControllerViewportTransitionTest,
- testing::Values(
- // no transition
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {2.0f, 2.0f},
- {4.0f, 4.0f},
- {0.0f, 0.0f}},
- // right boundary
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {3.0f, 0.0f},
- {4.0f, 2.0f},
- {1.0f, 0.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {3.0f, -1.0f},
- {4.0f, 1.0f},
- {1.0f, 0.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {3.0f, 1.0f},
- {4.0f, 3.0f},
- {1.0f, 0.0f}},
- // left boundary
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-3.0f, 0.0f},
- {0.0f, 2.0f},
- {-1.0f, 0.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-3.0f, -1.0f},
- {0.0f, 1.0f},
- {-1.0f, 0.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-3.0f, 1.0f},
- {0.0f, 3.0f},
- {-1.0f, 0.0f}},
- // bottom boundary
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {0.0f, 3.0f},
- {2.0f, 4.0f},
- {0.0f, 1.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-1.0f, 3.0f},
- {1.0f, 4.0f},
- {0.0f, 1.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {1.0f, 3.0f},
- {3.0f, 4.0f},
- {0.0f, 1.0f}},
- // top boundary
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {0.0f, -3.0f},
- {2.0f, 0.0f},
- {0.0f, -1.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-1.0f, -3.0f},
- {1.0f, 0.0f},
- {0.0f, -1.0f}},
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {1.0f, -3.0f},
- {3.0f, 0.0f},
- {0.0f, -1.0f}},
- // top-left corner
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-3.0f, -3.0f},
- {0.0f, 0.0f},
- {-1.0f, -1.0f}},
- // top-right corner
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {3.0f, -3.0f},
- {4.0f, 0.0f},
- {1.0f, -1.0f}},
- // bottom-right corner
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {3.0f, 3.0f},
- {4.0f, 4.0f},
- {1.0f, 1.0f}},
- // bottom-left corner
- MousePointerControllerTestParam{{2.0f, 2.0f},
- {-3.0f, 3.0f},
- {0.0f, 4.0f},
- {-1.0f, 1.0f}}));
-
class PointerControllerWindowInfoListenerTest : public Test {};
TEST_F(PointerControllerWindowInfoListenerTest,
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index f5913c763b82..4b3962e6dd74 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -22,6 +22,7 @@ import android.os.Build;
import com.android.aconfig.annotations.VisibleForTesting;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -68,14 +69,14 @@ public class AudioDevicePort extends AudioPort {
return new AudioDevicePort(
new AudioHandle(/* id= */ 0),
/* name= */ "testAudioDevicePort",
- /* profiles= */ null,
+ /* profiles= */ new ArrayList<>(),
/* gains= */ null,
/* type= */ AudioManager.DEVICE_OUT_SPEAKER,
/* address= */ "testAddress",
/* speakerLayoutChannelMask= */ speakerLayoutChannelMask,
/* encapsulationModes= */ null,
/* encapsulationMetadataTypes= */ null,
- /* descriptors= */ null);
+ /* descriptors= */ new ArrayList<>());
}
private final int mType;
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index aaedf21999f7..1c85c7b97a6e 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -21,6 +21,7 @@ import android.media.quality.IAmbientBacklightCallback;
import android.media.quality.IPictureProfileCallback;
import android.media.quality.ISoundProfileCallback;
import android.media.quality.ParamCapability;
+import android.media.quality.PictureProfileHandle;
import android.media.quality.PictureProfile;
import android.media.quality.SoundProfile;
@@ -38,6 +39,7 @@ interface IMediaQualityManager {
List<String> getPictureProfilePackageNames();
List<String> getPictureProfileAllowList();
void setPictureProfileAllowList(in List<String> packages);
+ PictureProfileHandle getPictureProfileHandle(in String id);
SoundProfile createSoundProfile(in SoundProfile pp);
void updateSoundProfile(in String id, in SoundProfile pp);
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 28fe9b6c8112..43e884a8271e 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -271,6 +271,17 @@ public final class MediaQualityManager {
}
}
+ /**
+ * Gets picture profile handle by profile ID.
+ * @hide
+ */
+ public PictureProfileHandle getPictureProfileHandle(String id) {
+ try {
+ return mService.getPictureProfileHandle(id);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Creates a picture profile and store it in the system.
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 898a8bf02edb..4de71235df8c 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -63,7 +63,7 @@ public class FrontendStatus {
FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO,
FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST,
FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS,
- FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXT})
+ FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXTENSION})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -317,7 +317,7 @@ public class FrontendStatus {
* Standard extension.
*/
@FlaggedApi(Flags.FLAG_TUNER_W_APIS)
- public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT =
+ public static final int FRONTEND_STATUS_TYPE_STANDARD_EXTENSION =
android.hardware.tv.tuner.FrontendStatusType.STANDARD_EXT;
/** @hide */
@@ -567,7 +567,7 @@ public class FrontendStatus {
private Long mIptvPacketsReceived;
private Integer mIptvWorstJitterMs;
private Integer mIptvAverageJitterMs;
- private StandardExt mStandardExt;
+ private StandardExtension mStandardExtension;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1298,12 +1298,12 @@ public class FrontendStatus {
*/
@NonNull
@FlaggedApi(Flags.FLAG_TUNER_W_APIS)
- public StandardExt getStandardExt() {
+ public StandardExtension getStandardExtension() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_4_0, "StandardExt status");
- if (mStandardExt == null) {
- throw new IllegalStateException("StandardExt status is empty");
+ TunerVersionChecker.TUNER_VERSION_4_0, "StandardExtension status");
+ if (mStandardExtension == null) {
+ throw new IllegalStateException("StandardExtension status is empty");
}
- return mStandardExt;
+ return mStandardExtension;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/StandardExt.java b/media/java/android/media/tv/tuner/frontend/StandardExtension.java
index 490727278b46..8bff3dd99cdb 100644
--- a/media/java/android/media/tv/tuner/frontend/StandardExt.java
+++ b/media/java/android/media/tv/tuner/frontend/StandardExtension.java
@@ -29,16 +29,16 @@ import android.media.tv.flags.Flags;
*/
@SystemApi
@FlaggedApi(Flags.FLAG_TUNER_W_APIS)
-public final class StandardExt {
- private final int mDvbsStandardExt;
- private final int mDvbtStandardExt;
+public final class StandardExtension {
+ private final int mDvbsStandardExtension;
+ private final int mDvbtStandardExtension;
/**
* Private constructor called by JNI only.
*/
- private StandardExt(int dvbsStandardExt, int dvbtStandardExt) {
- mDvbsStandardExt = dvbsStandardExt;
- mDvbtStandardExt = dvbtStandardExt;
+ private StandardExtension(int dvbsStandardExtension, int dvbtStandardExtension) {
+ mDvbsStandardExtension = dvbsStandardExtension;
+ mDvbtStandardExtension = dvbtStandardExtension;
}
/**
@@ -50,11 +50,11 @@ public final class StandardExt {
* @see android.media.tv.tuner.frontend.DvbsFrontendSettings
*/
@DvbsFrontendSettings.Standard
- public int getDvbsStandardExt() {
- if (mDvbsStandardExt == FrontendDvbsStandard.UNDEFINED) {
+ public int getDvbsStandardExtension() {
+ if (mDvbsStandardExtension == FrontendDvbsStandard.UNDEFINED) {
throw new IllegalStateException("No DVB-S standard transition");
}
- return mDvbsStandardExt;
+ return mDvbsStandardExtension;
}
/**
@@ -66,10 +66,10 @@ public final class StandardExt {
* @see android.media.tv.tuner.frontend.DvbtFrontendSettings
*/
@DvbtFrontendSettings.Standard
- public int getDvbtStandardExt() {
- if (mDvbtStandardExt == FrontendDvbtStandard.UNDEFINED) {
+ public int getDvbtStandardExtension() {
+ if (mDvbtStandardExtension == FrontendDvbtStandard.UNDEFINED) {
throw new IllegalStateException("No DVB-T standard transition");
}
- return mDvbtStandardExt;
+ return mDvbtStandardExtension;
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 80ca4f239a26..2fe069af638a 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -2940,10 +2940,10 @@ jobject JTuner::getFrontendStatus(jintArray types) {
break;
}
case FrontendStatus::Tag::standardExt: {
- jfieldID field = env->GetFieldID(clazz, "mStandardExt",
- "Landroid/media/tv/tuner/frontend/StandardExt;");
+ jfieldID field = env->GetFieldID(clazz, "mStandardExtension",
+ "Landroid/media/tv/tuner/frontend/StandardExtension;");
ScopedLocalRef standardExtClazz(env,
- env->FindClass("android/media/tv/tuner/frontend/StandardExt"));
+ env->FindClass("android/media/tv/tuner/frontend/StandardExtension"));
jmethodID initStandardExt = env->GetMethodID(standardExtClazz.get(), "<init>",
"(II)V");
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 2d1fbf9e7f66..6b41ddde5cc8 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -363,6 +363,7 @@ LIBANDROID {
APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
+ APerformanceHint_borrowSessionFromJava; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -383,6 +384,8 @@ LIBANDROID_PLATFORM {
APerformanceHint_setUseFMQForTesting;
APerformanceHint_getRateLimiterPropertiesForTesting;
APerformanceHint_setUseNewLoadHintBehaviorForTesting;
+ APerformanceHint_closeSessionFromJava;
+ APerformanceHint_createSessionFromJava;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index e2fa94dd39bb..bc1945e37072 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -36,6 +36,7 @@
#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
#include <inttypes.h>
+#include <jni_wrappers.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
@@ -137,10 +138,14 @@ public:
APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag = hal::SessionTag::APP);
+ hal::SessionTag tag = hal::SessionTag::APP,
+ bool isJava = false);
+ APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj);
+
int64_t getPreferredRateNanos() const;
FMQWrapper& getFMQWrapper();
bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
+ void initJava(JNIEnv* _Nonnull env);
private:
// Necessary to create an empty binder object
@@ -161,13 +166,16 @@ private:
FMQWrapper mFMQWrapper;
double mHintBudget = kMaxLoadHintsPerInterval;
int64_t mLastBudgetReplenish = 0;
+ bool mJavaInitialized = false;
+ jclass mJavaSessionClazz;
+ jfieldID mJavaSessionNativePtr;
};
struct APerformanceHintSession {
public:
APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session, int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig);
APerformanceHintSession() = delete;
~APerformanceHintSession();
@@ -181,6 +189,7 @@ public:
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
int reportActualWorkDuration(AWorkDuration* workDuration);
+ bool isJava();
private:
friend struct APerformanceHintManager;
@@ -203,6 +212,8 @@ private:
std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
+ // Is this session backing an SDK wrapper object
+ const bool mIsJava;
std::string mSessionName;
static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
@@ -299,7 +310,7 @@ bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hi
APerformanceHintSession* APerformanceHintManager::createSession(
const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag) {
+ hal::SessionTag tag, bool isJava) {
std::vector<int32_t> tids(threadIds, threadIds + size);
std::shared_ptr<IHintSession> session;
ndk::ScopedAStatus ret;
@@ -312,7 +323,7 @@ APerformanceHintSession* APerformanceHintManager::createSession(
return nullptr;
}
auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
- initialTargetWorkDurationNanos,
+ initialTargetWorkDurationNanos, isJava,
sessionConfig.id == -1
? std::nullopt
: std::make_optional<hal::SessionConfig>(
@@ -324,6 +335,18 @@ APerformanceHintSession* APerformanceHintManager::createSession(
return out;
}
+APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ initJava(env);
+ LOG_ALWAYS_FATAL_IF(!env->IsInstanceOf(sessionObj, mJavaSessionClazz),
+ "Wrong java type passed to APerformanceHint_getSessionFromJava");
+ APerformanceHintSession* out = reinterpret_cast<APerformanceHintSession*>(
+ env->GetLongField(sessionObj, mJavaSessionNativePtr));
+ LOG_ALWAYS_FATAL_IF(out == nullptr, "Java-wrapped native hint session is nullptr");
+ LOG_ALWAYS_FATAL_IF(!out->isJava(), "Unmanaged native hint session returned from Java SDK");
+ return out;
+}
+
int64_t APerformanceHintManager::getPreferredRateNanos() const {
return mPreferredRateNanos;
}
@@ -332,13 +355,23 @@ FMQWrapper& APerformanceHintManager::getFMQWrapper() {
return mFMQWrapper;
}
+void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) {
+ if (mJavaInitialized) {
+ return;
+ }
+ jclass sessionClazz = FindClassOrDie(env, "android/os/PerformanceHintManager$Session");
+ mJavaSessionClazz = MakeGlobalRefOrDie(env, sessionClazz);
+ mJavaSessionNativePtr = GetFieldIDOrDie(env, mJavaSessionClazz, "mNativeSessionPtr", "J");
+ mJavaInitialized = true;
+}
+
// ===================================== APerformanceHintSession implementation
constexpr int kNumEnums = enum_size<hal::SessionHint>();
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig)
: mHintManager(hintManager),
mHintSession(std::move(session)),
@@ -347,6 +380,7 @@ APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> h
mFirstTargetMetTimestamp(0),
mLastTargetMetTimestamp(0),
mLastHintSentTimestamp(std::vector<int64_t>(kNumEnums, 0)),
+ mIsJava(isJava),
mSessionConfig(sessionConfig) {
if (sessionConfig->id > INT32_MAX) {
ALOGE("Session ID too large, must fit 32-bit integer");
@@ -401,6 +435,10 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano
return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
}
+bool APerformanceHintSession::isJava() {
+ return mIsJava;
+}
+
int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now,
const char*) {
std::scoped_lock lock(sHintMutex);
@@ -826,6 +864,22 @@ APerformanceHintSession* APerformanceHint_createSessionInternal(
static_cast<hal::SessionTag>(tag));
}
+APerformanceHintSession* APerformanceHint_createSessionFromJava(
+ APerformanceHintManager* manager, const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos) {
+ VALIDATE_PTR(manager)
+ VALIDATE_PTR(threadIds)
+ return manager->createSession(threadIds, size, initialTargetWorkDurationNanos,
+ hal::SessionTag::APP, true);
+}
+
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ VALIDATE_PTR(env)
+ VALIDATE_PTR(sessionObj)
+ return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
+}
+
int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
VALIDATE_PTR(manager)
return manager->getPreferredRateNanos();
@@ -846,6 +900,16 @@ int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
void APerformanceHint_closeSession(APerformanceHintSession* session) {
VALIDATE_PTR(session)
+ if (session->isJava()) {
+ LOG_ALWAYS_FATAL("%s: Java-owned PerformanceHintSession cannot be closed in native",
+ __FUNCTION__);
+ return;
+ }
+ delete session;
+}
+
+void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session) {
+ VALIDATE_PTR(session)
delete session;
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index c90ba8249d54..32d4580f67ec 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -464,5 +464,14 @@ public class GlobalSettingsValidators {
));
VALIDATORS.put(Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.HEARING_DEVICE_LOCAL_NOTIFICATION, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.WEAR_SYSTEM_STATUS_TRAY_CONFIGURATION,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(
+ Global.Wearable.STATUS_TRAY_CONFIGURATION_DEFAULT),
+ String.valueOf(
+ Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
+ }));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 603a91195d04..03fea37deaf5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2434,28 +2434,40 @@ public class SettingsProvider extends ContentProvider {
context.checkCallingOrSelfPermission(
Manifest.permission.WRITE_DEVICE_CONFIG)
== PackageManager.PERMISSION_GRANTED;
- boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
+ // Only the shell user and tests request the allowlist permission; this is used to force
+ // the WRITE_ALLOWLISTED_DEVICE_CONFIG path to log any flags that need to be allowlisted.
+ boolean isRestrictedShell = android.security.Flags.protectDeviceConfigFlags()
+ && hasAllowlistPermission;
- if (isRoot) {
- return;
- }
-
- if (hasWritePermission) {
+ if (!isRestrictedShell && hasWritePermission) {
assertCallingUserDenyList(flags);
} else if (hasAllowlistPermission) {
for (String flag : flags) {
boolean namespaceAllowed = false;
- for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
- if (flag.startsWith(allowlistedPrefix)) {
+ if (isRestrictedShell) {
+ int delimiterIndex = flag.indexOf("/");
+ String flagNamespace;
+ if (delimiterIndex != -1) {
+ flagNamespace = flag.substring(0, delimiterIndex);
+ } else {
+ flagNamespace = flag;
+ }
+ if (WritableNamespaces.ALLOWLIST.contains(flagNamespace)) {
namespaceAllowed = true;
- break;
+ }
+ } else {
+ for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
+ if (flag.startsWith(allowlistedPrefix)) {
+ namespaceAllowed = true;
+ break;
+ }
}
}
if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
- throw new SecurityException("Permission denial for flag '"
- + flag
- + "'; allowlist permission granted, but must add flag to the allowlist.");
+ Slog.wtf(LOG_TAG, "Permission denial for flag '" + flag
+ + "'; allowlist permission granted, but must add flag to the "
+ + "allowlist");
}
}
assertCallingUserDenyList(flags);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
new file mode 100644
index 000000000000..d835c5f5c179
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of namespaces in which any flag can be written by adb without root
+ * permissions.
+ * <p>
+ * A security review is required for any namespace that's added to this list. To add to
+ * the list, create a change and tag the OWNER. In the commit message, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * allowlisted.
+ */
+final class WritableNamespaces {
+ public static final Set<String> ALLOWLIST =
+ new ArraySet<String>(Arrays.asList(
+ "exo"
+ ));
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9de7faf04df6..a62b7fd3db81 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -635,7 +635,8 @@ public class SettingsBackupTest {
Settings.Global.Wearable.WEAR_MEDIA_SESSIONS_PACKAGE,
Settings.Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED,
Settings.Global.Wearable.CONNECTIVITY_KEEP_DATA_ON,
- Settings.Global.Wearable.PHONE_SWITCHING_REQUEST_SOURCE);
+ Settings.Global.Wearable.PHONE_SWITCHING_REQUEST_SOURCE,
+ Settings.Global.Wearable.WEAR_SYSTEM_STATUS_TRAY_CONFIGURATION);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1070ebdbb946..fef0f8c7857a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -242,6 +242,8 @@
<uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL" />
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.COPY_ACCOUNTS" />
+ <uses-permission android:name="android.permission.REMOVE_ACCOUNTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
<uses-permission android:name="android.permission.FRAME_STATS" />
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 67666c3db81f..c82c63c7c78b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1798,3 +1798,17 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "gsf_bouncer"
+ namespace: "systemui"
+ description: "Applies GSF font styles to Bouncer surfaces."
+ bug: "379364381"
+}
+
+flag {
+ name: "gsf_quick_settings"
+ namespace: "systemui"
+ description: "Applies GSF font styles to Quick Settings surfaces."
+ bug: "379364381"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 9390664d1283..597cbf24729b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -31,6 +31,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -147,7 +148,7 @@ constructor(
} else {
val scaleFactor = authController.scaleFactor
val bottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom)
val heightPx = windowViewBounds.bottom.toFloat()
Pair(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index b3fd097946d0..4bccac1e3ba0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -430,14 +430,6 @@ internal class MutableSceneTransitionLayoutStateImpl(
check(transitionStates.size == 1)
check(transitionStates[0] is TransitionState.Idle)
transitionStates = listOf(transition)
- } else if (currentState == transition.replacedTransition) {
- // Replace the transition.
- transitionStates =
- transitionStates.subList(0, transitionStates.lastIndex) + transition
-
- // Make sure it is removed from the finishedTransitions set if it was already
- // finished.
- finishedTransitions.remove(currentState)
} else {
// Append the new transition.
transitionStates = transitionStates + transition
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index b87cc5c88335..3622369b8ff9 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -132,6 +132,9 @@ class InterruptionHandlerTest {
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
.containsExactly(
+ // Initial transition, A => B.
+ Triple(SceneA, SceneB, SceneB),
+
// Initial transition reversed, B back to A.
Triple(SceneA, SceneB, SceneA),
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml b/packages/SystemUI/customization/res/values-land/dimens.xml
index 4bd9ca049f55..50f220c882bd 100644
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/flicker-service/res/xml/network_security_config.xml
+++ b/packages/SystemUI/customization/res/values-land/dimens.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,8 +14,6 @@
~ limitations under the License.
-->
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
+<resources>
+ <dimen name="lock_icon_margin_bottom">24dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
index 18073adf9e7a..876028195ff3 100644
--- a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
@@ -17,4 +17,5 @@
<resources>
<dimen name="keyguard_smartspace_top_offset">0dp</dimen>
<dimen name="status_view_margin_horizontal">8dp</dimen>
+ <dimen name="lock_icon_margin_bottom">60dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
index 041ae62670e5..21b4c7165226 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/res/values/dimens.xml
@@ -40,4 +40,5 @@
<dimen name="date_weather_view_height">24dp</dimen>
<dimen name="enhanced_smartspace_height">104dp</dimen>
<dimen name="status_view_margin_horizontal">0dp</dimen>
+ <dimen name="lock_icon_margin_bottom">74dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt
new file mode 100644
index 000000000000..e17b66e90c2d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.data.repository
+
+import android.content.pm.UserInfo
+import android.os.Build
+import android.os.UserHandle
+import android.os.UserManager
+import android.os.userManager
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DevelopmentSettingRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val underTest = kosmos.developmentSettingRepository
+
+ @Test
+ fun nonAdminUser_unrestricted_neverDevelopmentEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = nonAdminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = false)
+
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isFalse()
+ }
+ }
+
+ @Test
+ fun nonAdminUser_restricted_neverDevelopmentEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = nonAdminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = true)
+
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isFalse()
+ }
+ }
+
+ @Test
+ fun adminUser_unrestricted_defaultValueOfSetting() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = adminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = false)
+
+ val defaultValue = Build.TYPE == "eng"
+
+ assertThat(settingEnabled).isEqualTo(defaultValue)
+ }
+ }
+
+ @Test
+ fun adminUser_unrestricted_enabledTracksSetting() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = adminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = false)
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isTrue()
+ }
+ }
+
+ @Test
+ fun adminUser_restricted_neverDevelopmentEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val userInfo = adminUserInfo
+ val settingEnabled by
+ collectLastValue(underTest.isDevelopmentSettingEnabled(userInfo))
+
+ setUserRestriction(userInfo.userHandle, restricted = true)
+
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(false)
+ assertThat(settingEnabled).isFalse()
+
+ setSettingValue(true)
+ assertThat(settingEnabled).isFalse()
+ }
+ }
+
+ private companion object {
+ const val USER_RESTRICTION = UserManager.DISALLOW_DEBUGGING_FEATURES
+ const val SETTING_NAME = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+
+ val adminUserInfo =
+ UserInfo(
+ /* id= */ 10,
+ /* name= */ "",
+ /* flags */ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+ val nonAdminUserInfo =
+ UserInfo(/* id= */ 11, /* name= */ "", /* flags */ UserInfo.FLAG_FULL)
+
+ fun Kosmos.setUserRestriction(userHandle: UserHandle, restricted: Boolean) {
+ userManager.stub {
+ on { hasUserRestrictionForUser(eq(USER_RESTRICTION), eq(userHandle)) } doReturn
+ restricted
+ }
+ }
+
+ fun Kosmos.setSettingValue(enabled: Boolean) {
+ fakeGlobalSettings.putInt(SETTING_NAME, if (enabled) 1 else 0)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt
new file mode 100644
index 000000000000..f29dabe98664
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.domain.interactor
+
+import android.content.ClipData
+import android.content.ClipDescription
+import android.content.clipboardManager
+import android.content.pm.UserInfo
+import android.content.res.mainResources
+import android.os.Build
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.development.shared.model.BuildNumber
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class BuildNumberInteractorTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().apply {
+ fakeUserRepository.setUserInfos(listOf(adminUserInfo, nonAdminUserInfo))
+ }
+
+ private val expectedBuildNumber =
+ BuildNumber(
+ kosmos.mainResources.getString(
+ R.string.bugreport_status,
+ Build.VERSION.RELEASE_OR_CODENAME,
+ Build.ID,
+ )
+ )
+
+ private val clipLabel =
+ kosmos.mainResources.getString(
+ com.android.systemui.res.R.string.build_number_clip_data_label
+ )
+
+ private val underTest = kosmos.buildNumberInteractor
+
+ @Test
+ fun nonAdminUser_settingEnabled_buildNumberNull() =
+ with(kosmos) {
+ testScope.runTest {
+ val buildNumber by collectLastValue(underTest.buildNumber)
+
+ fakeUserRepository.setSelectedUserInfo(nonAdminUserInfo)
+ setSettingValue(true)
+
+ assertThat(buildNumber).isNull()
+ }
+ }
+
+ @Test
+ fun adminUser_buildNumberCorrect_onlyWhenSettingEnabled() =
+ with(kosmos) {
+ testScope.runTest {
+ val buildNumber by collectLastValue(underTest.buildNumber)
+
+ fakeUserRepository.setSelectedUserInfo(adminUserInfo)
+
+ setSettingValue(false)
+ assertThat(buildNumber).isNull()
+
+ setSettingValue(true)
+ assertThat(buildNumber).isEqualTo(expectedBuildNumber)
+ }
+ }
+
+ @Test
+ fun copyToClipboard() =
+ with(kosmos) {
+ testScope.runTest {
+ fakeUserRepository.setSelectedUserInfo(adminUserInfo)
+
+ underTest.copyBuildNumber()
+ runCurrent()
+
+ val argumentCaptor = argumentCaptor<ClipData>()
+
+ verify(clipboardManager).setPrimaryClip(argumentCaptor.capture())
+
+ with(argumentCaptor.firstValue) {
+ assertThat(description.label).isEqualTo(clipLabel)
+ assertThat(description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN))
+ .isTrue()
+ assertThat(itemCount).isEqualTo(1)
+ assertThat(getItemAt(0).text).isEqualTo(expectedBuildNumber.value)
+ }
+ }
+ }
+
+ private companion object {
+ const val SETTING_NAME = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+
+ val adminUserInfo =
+ UserInfo(
+ /* id= */ 10,
+ /* name= */ "",
+ /* flags */ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+ val nonAdminUserInfo =
+ UserInfo(/* id= */ 11, /* name= */ "", /* flags */ UserInfo.FLAG_FULL)
+
+ fun Kosmos.setSettingValue(enabled: Boolean) {
+ fakeGlobalSettings.putInt(SETTING_NAME, if (enabled) 1 else 0)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
index c39c3fe5f527..2d54337def13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -36,6 +36,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -43,6 +44,7 @@ import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.runner.RunWith
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
@@ -84,6 +86,12 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys
@Before
fun setup() {
underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+ overrideResource(R.integer.udfps_padding_debounce_duration, 0)
+ }
+
+ @After
+ fun teardown() {
+ mContext.orCreateTestableResources.removeOverride(R.integer.udfps_padding_debounce_duration)
}
@Test
@@ -118,6 +126,7 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys
runCurrent()
assertThat(visible).isFalse()
}
+
fun fpNotRunning_overlayNotVisible() =
testScope.runTest {
val visible by collectLastValue(underTest.visible)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
index b0959e4eea0b..d42b538cf355 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
@@ -27,10 +27,13 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepos
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,6 +46,16 @@ class DeviceEntryForegroundViewModelTest : SysuiTestCase() {
private val underTest: DeviceEntryForegroundViewModel =
kosmos.deviceEntryForegroundIconViewModel
+ @Before
+ fun setup() {
+ context.orCreateTestableResources.addOverride(R.integer.udfps_padding_debounce_duration, 0)
+ }
+
+ @After
+ fun teardown() {
+ context.orCreateTestableResources.removeOverride(R.integer.udfps_padding_debounce_duration)
+ }
+
@Test
fun aodIconColorWhite() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
index 450aadd70171..ebc00c3897cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.UserHandle
+import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import kotlinx.coroutines.CoroutineDispatcher
@@ -33,14 +34,29 @@ import kotlinx.coroutines.CoroutineDispatcher
class FakeNoteTaskBubbleController(
unUsed1: Context,
unsUsed2: CoroutineDispatcher,
- private val optionalBubbles: Optional<Bubbles>
+ private val optionalBubbles: Optional<Bubbles>,
) : NoteTaskBubblesController(unUsed1, unsUsed2) {
override suspend fun areBubblesAvailable() = optionalBubbles.isPresent
- override suspend fun showOrHideAppBubble(intent: Intent, userHandle: UserHandle, icon: Icon) {
+ override suspend fun showOrHideAppBubble(
+ intent: Intent,
+ userHandle: UserHandle,
+ icon: Icon,
+ bubbleExpandBehavior: NoteTaskBubbleExpandBehavior,
+ ) {
optionalBubbles.ifPresentOrElse(
- { bubbles -> bubbles.showOrHideAppBubble(intent, userHandle, icon) },
- { throw IllegalAccessException() }
+ { bubbles ->
+ if (
+ bubbleExpandBehavior == NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED &&
+ bubbles.isBubbleExpanded(
+ Bubble.getAppBubbleKeyForApp(intent.`package`, userHandle)
+ )
+ ) {
+ return@ifPresentOrElse
+ }
+ bubbles.showOrHideAppBubble(intent, userHandle, icon)
+ },
+ { throw IllegalAccessException() },
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
index 9ef6b9c13315..e55d6ad6c5a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
@@ -21,9 +21,9 @@ import android.graphics.drawable.Icon
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesService
+import com.android.systemui.res.R
import com.android.wm.shell.bubbles.Bubbles
import com.google.common.truth.Truth.assertThat
import java.util.Optional
@@ -33,6 +33,9 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.whenever
/** atest SystemUITests:NoteTaskBubblesServiceTest */
@SmallTest
@@ -61,12 +64,40 @@ internal class NoteTaskBubblesServiceTest : SysuiTestCase() {
}
@Test
- fun showOrHideAppBubble() {
+ fun showOrHideAppBubble_defaultExpandBehavior_shouldCallBubblesApi() {
val intent = Intent()
val user = UserHandle.SYSTEM
val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+ val bubbleExpandBehavior = NoteTaskBubbleExpandBehavior.DEFAULT
+ whenever(bubbles.isBubbleExpanded(any())).thenReturn(false)
+
+ createServiceBinder().showOrHideAppBubble(intent, user, icon, bubbleExpandBehavior)
+
+ verify(bubbles).showOrHideAppBubble(intent, user, icon)
+ }
+
+ @Test
+ fun showOrHideAppBubble_keepIfExpanded_bubbleShown_shouldNotCallBubblesApi() {
+ val intent = Intent().apply { setPackage("test") }
+ val user = UserHandle.SYSTEM
+ val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+ val bubbleExpandBehavior = NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED
+ whenever(bubbles.isBubbleExpanded(any())).thenReturn(true)
+
+ createServiceBinder().showOrHideAppBubble(intent, user, icon, bubbleExpandBehavior)
+
+ verify(bubbles, never()).showOrHideAppBubble(intent, user, icon)
+ }
+
+ @Test
+ fun showOrHideAppBubble_keepIfExpanded_bubbleNotShown_shouldCallBubblesApi() {
+ val intent = Intent().apply { setPackage("test") }
+ val user = UserHandle.SYSTEM
+ val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+ val bubbleExpandBehavior = NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED
+ whenever(bubbles.isBubbleExpanded(any())).thenReturn(false)
- createServiceBinder().showOrHideAppBubble(intent, user, icon)
+ createServiceBinder().showOrHideAppBubble(intent, user, icon, bubbleExpandBehavior)
verify(bubbles).showOrHideAppBubble(intent, user, icon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 8f4078b88fc0..d3578fb9f69b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -19,6 +19,7 @@ import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.NoteTaskEntryPoint.QS_NOTES_TILE
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -44,10 +45,19 @@ internal class NoteTaskInfoTest : SysuiTestCase() {
}
@Test
- fun launchMode_keyguardUnlocked_launchModeAppBubble() {
+ fun launchMode_keyguardUnlocked_launchModeAppBubble_withDefaultExpandBehavior() {
val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false)
- assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
+ assertThat(underTest.launchMode)
+ .isEqualTo(NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.DEFAULT))
+ }
+
+ @Test
+ fun launchMode_keyguardUnlocked_qsTileEntryPoint_launchModeAppBubble_withKeepIfExpandedExpandBehavior() {
+ val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false, entryPoint = QS_NOTES_TILE)
+
+ assertThat(underTest.launchMode)
+ .isEqualTo(NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED))
}
private companion object {
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 235015b5286f..70ae5c1832af 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -91,7 +91,6 @@
<dimen name="notification_blocker_channel_list_height">128dp</dimen>
<dimen name="keyguard_indication_margin_bottom">8dp</dimen>
- <dimen name="lock_icon_margin_bottom">24dp</dimen>
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_container_horizontal_margin">48dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 75bee9f9266a..055c3a641d1b 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -20,7 +20,6 @@
<!-- keyguard-->
<dimen name="keyguard_indication_margin_bottom">25dp</dimen>
<dimen name="ambient_indication_margin_bottom">115dp</dimen>
- <dimen name="lock_icon_margin_bottom">60dp</dimen>
<!-- margin from keyguard status bar to clock. For split shade it should be
keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
<dimen name="keyguard_clock_top_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 580f6d302b37..8cab15506d20 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -948,7 +948,6 @@
<dimen name="keyguard_translate_distance_on_swipe_up">-200dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
- <dimen name="lock_icon_margin_bottom">74dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
@@ -2057,6 +2056,9 @@
<!-- UDFPS view attributes -->
<!-- UDFPS icon size in microns/um -->
<dimen name="udfps_icon_size" format="float">6000</dimen>
+ <!-- Limits the updates to at most one update per debounce duration to avoid too many
+ updates due to quick changes to padding. -->
+ <integer name="udfps_padding_debounce_duration">100</integer>
<!-- Microns/ums (1000 um = 1mm) per pixel for the given device. If unspecified, UI that
relies on this value will not be sized correctly. -->
<item name="pixel_pitch" format="float" type="dimen">-1</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 245ba0aca876..c3f4222a5eb8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3214,6 +3214,9 @@
<!-- Text to display when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_copy_toast">Build number copied to clipboard.</string>
+ <!-- Text for accessibility action for copying content to clipboard [CHAR LIMIT=NONE]-->
+ <string name="copy_to_clipboard_a11y_action">copy to clipboard.</string>
+
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="basic_status">Open conversation</string>
<!--Title text for Conversation widget set up screen [CHAR LIMIT=180] -->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index abbbd730c47e..976329580c60 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -36,8 +36,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlin.math.max
/** Encapsulates business logic for interacting with the UDFPS overlay. */
@SysUISingleton
@@ -124,8 +126,9 @@ constructor(
udfpsOverlayParams.map { params ->
val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
val nativePadding = (sensorWidth - iconSize) / 2
- (nativePadding * params.scaleFactor).toInt()
- }
+ // padding can be negative when udfpsOverlayParams has not been initialized yet.
+ max(0, (nativePadding * params.scaleFactor).toInt())
+ }.distinctUntilChanged()
companion object {
private const val TAG = "UdfpsOverlayInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt b/packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt
new file mode 100644
index 000000000000..a8fa9797ebfd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/data/repository/DevelopmentSettingRepository.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.data.repository
+
+import android.content.pm.UserInfo
+import android.os.Build
+import android.os.UserManager
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class DevelopmentSettingRepository
+@Inject
+constructor(
+ private val globalSettings: GlobalSettings,
+ private val userManager: UserManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+ private val settingFlow = globalSettings.observerFlow(SETTING)
+
+ /**
+ * Indicates whether development settings is enabled for this user. The conditions are:
+ * * Setting is enabled (defaults to true in eng builds)
+ * * User is an admin
+ * * User is not restricted from Debugging features.
+ */
+ fun isDevelopmentSettingEnabled(userInfo: UserInfo): Flow<Boolean> {
+ return settingFlow
+ .emitOnStart()
+ .map { checkDevelopmentSettingEnabled(userInfo) }
+ .flowOn(backgroundDispatcher)
+ }
+
+ private suspend fun checkDevelopmentSettingEnabled(userInfo: UserInfo): Boolean {
+ val hasUserRestriction =
+ withContext(backgroundDispatcher) {
+ userManager.hasUserRestrictionForUser(
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ userInfo.userHandle,
+ )
+ }
+ val isSettingEnabled =
+ withContext(backgroundDispatcher) {
+ globalSettings.getInt(SETTING, DEFAULT_ENABLED) != 0
+ }
+ val isAdmin = userInfo.isAdmin
+ return isAdmin && !hasUserRestriction && isSettingEnabled
+ }
+
+ private companion object {
+ val DEFAULT_ENABLED = if (Build.TYPE == "eng") 1 else 0
+
+ const val SETTING = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt b/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
new file mode 100644
index 000000000000..4d786fe76bc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/domain/interactor/BuildNumberInteractor.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.domain.interactor
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.res.Resources
+import android.os.Build
+import android.os.UserHandle
+import com.android.internal.R as InternalR
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.development.data.repository.DevelopmentSettingRepository
+import com.android.systemui.development.shared.model.BuildNumber
+import com.android.systemui.res.R as SystemUIR
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.user.utils.UserScopedService
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class BuildNumberInteractor
+@Inject
+constructor(
+ repository: DevelopmentSettingRepository,
+ @Main resources: Resources,
+ private val userRepository: UserRepository,
+ private val clipboardManagerProvider: UserScopedService<ClipboardManager>,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ /**
+ * Build number, or `null` if Development Settings is not enabled for the current user.
+ *
+ * @see DevelopmentSettingRepository.isDevelopmentSettingEnabled
+ */
+ val buildNumber: Flow<BuildNumber?> =
+ userRepository.selectedUserInfo
+ .flatMapConcat { userInfo -> repository.isDevelopmentSettingEnabled(userInfo) }
+ .map { enabled -> buildText.takeIf { enabled } }
+
+ private val buildText =
+ BuildNumber(
+ resources.getString(
+ InternalR.string.bugreport_status,
+ Build.VERSION.RELEASE_OR_CODENAME,
+ Build.ID,
+ )
+ )
+
+ private val clipLabel = resources.getString(SystemUIR.string.build_number_clip_data_label)
+
+ private val currentUserHandle: UserHandle
+ get() = userRepository.getSelectedUserInfo().userHandle
+
+ /**
+ * Copy to the clipboard the build number for the current user.
+ *
+ * This can be performed regardless of the current user having Development Settings enabled
+ */
+ suspend fun copyBuildNumber() {
+ withContext(backgroundDispatcher) {
+ clipboardManagerProvider
+ .forUser(currentUserHandle)
+ .setPrimaryClip(ClipData.newPlainText(clipLabel, buildText.value))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt b/packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt
new file mode 100644
index 000000000000..5bd713fc4840
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/shared/model/BuildNumber.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.shared.model
+
+@JvmInline value class BuildNumber(val value: String)
diff --git a/packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt b/packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt
new file mode 100644
index 000000000000..72e1cede0002
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/ui/compose/BuildNumber.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.ui.compose
+
+import androidx.compose.foundation.basicMarquee
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.material3.Text
+import androidx.compose.material3.minimumInteractiveComponentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.onLongClick
+import androidx.compose.ui.semantics.semantics
+import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
+
+@Composable
+fun BuildNumber(
+ viewModelFactory: BuildNumberViewModel.Factory,
+ textColor: Color,
+ modifier: Modifier = Modifier,
+) {
+ val viewModel = rememberViewModel(traceName = "BuildNumber") { viewModelFactory.create() }
+
+ val buildNumber = viewModel.buildNumber
+
+ if (buildNumber != null) {
+ val haptics = LocalHapticFeedback.current
+ val copyToClipboardActionLabel = stringResource(id = R.string.copy_to_clipboard_a11y_action)
+
+ Text(
+ text = buildNumber.value,
+ modifier =
+ modifier
+ .focusable()
+ .wrapContentWidth()
+ // Using this instead of combinedClickable because this node should not support
+ // single click
+ .pointerInput(Unit) {
+ detectLongPressGesture {
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress)
+ viewModel.onBuildNumberLongPress()
+ }
+ }
+ .semantics {
+ onLongClick(copyToClipboardActionLabel) {
+ viewModel.onBuildNumberLongPress()
+ true
+ }
+ }
+ .basicMarquee(iterations = 1, initialDelayMillis = 2000)
+ .minimumInteractiveComponentSize(),
+ color = textColor,
+ maxLines = 1,
+ )
+ } else {
+ Spacer(modifier)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt b/packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt
new file mode 100644
index 000000000000..68c51ea80ffd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModel.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.systemui.development.domain.interactor.BuildNumberInteractor
+import com.android.systemui.development.shared.model.BuildNumber
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+/** View model for UI that (optionally) shows the build number and copies it on long press. */
+class BuildNumberViewModel
+@AssistedInject
+constructor(private val buildNumberInteractor: BuildNumberInteractor) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("BuildNumberViewModel")
+
+ private val copyRequests = Channel<Unit>()
+
+ val buildNumber: BuildNumber? by
+ hydrator.hydratedStateOf(
+ traceName = "buildNumber",
+ initialValue = null,
+ source = buildNumberInteractor.buildNumber,
+ )
+
+ fun onBuildNumberLongPress() {
+ copyRequests.trySend(Unit)
+ }
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch {
+ copyRequests.receiveAsFlow().collect { buildNumberInteractor.copyBuildNumber() }
+ }
+ awaitCancellation()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): BuildNumberViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index b51bb7b229ee..aa7eb2933992 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -28,6 +28,7 @@ import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -115,7 +116,7 @@ constructor(
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom)
val bounds = windowManager.currentWindowMetrics.bounds
var widthPixels = bounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 5065fcbbac93..19652525bee0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -31,8 +31,10 @@ import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -40,6 +42,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/** Models the UI state for the device entry icon foreground view (displayed icon). */
+@OptIn(FlowPreview::class)
@ExperimentalCoroutinesApi
@SysUISingleton
class DeviceEntryForegroundViewModel
@@ -97,7 +100,7 @@ constructor(
private val padding: Flow<Int> =
deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
if (udfpsSupported) {
- udfpsOverlayInteractor.iconPadding
+ udfpsOverlayInteractor.iconPadding.debounce(udfpsPaddingDebounceDuration.toLong())
} else {
configurationInteractor.scaleForResolution.map { scale ->
(context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
@@ -120,6 +123,9 @@ constructor(
)
}
+ private val udfpsPaddingDebounceDuration: Int
+ get() = context.resources.getInteger(R.integer.udfps_padding_debounce_duration)
+
data class ForegroundIconViewModel(
val type: DeviceEntryIconView.IconType,
val useAodVariant: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl b/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl
index 3e947d9d2b16..7803f229dda7 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl
+++ b/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl
@@ -16,6 +16,7 @@
package com.android.systemui.notetask;
+import com.android.systemui.notetask.NoteTaskBubbleExpandBehavior;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
@@ -25,5 +26,6 @@ interface INoteTaskBubblesService {
boolean areBubblesAvailable();
- void showOrHideAppBubble(in Intent intent, in UserHandle userHandle, in Icon icon);
+ void showOrHideAppBubble(in Intent intent, in UserHandle userHandle, in Icon icon,
+ in NoteTaskBubbleExpandBehavior bubbleExpandBehavior);
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl
new file mode 100644
index 000000000000..86a06a568a4b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.aidl
@@ -0,0 +1,3 @@
+package com.android.systemui.notetask;
+
+parcelable NoteTaskBubbleExpandBehavior; \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt
new file mode 100644
index 000000000000..63b38a1b73f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubbleExpandBehavior.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notetask
+
+import android.os.Parcel
+import android.os.Parcelable
+
+enum class NoteTaskBubbleExpandBehavior : Parcelable {
+ /**
+ * The default bubble expand behavior for note task bubble: The bubble will collapse if there is
+ * already an expanded bubble, The bubble will expand if there is a collapsed bubble.
+ */
+ DEFAULT,
+ /**
+ * The special bubble expand behavior for note task bubble: The bubble will stay expanded, not
+ * collapse, if there is already an expanded bubble, The bubble will expand if there is a
+ * collapsed bubble.
+ */
+ KEEP_IF_EXPANDED;
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeString(name)
+ }
+
+ companion object CREATOR : Parcelable.Creator<NoteTaskBubbleExpandBehavior> {
+ override fun createFromParcel(parcel: Parcel?): NoteTaskBubbleExpandBehavior {
+ return parcel?.readString()?.let { valueOf(it) } ?: DEFAULT
+ }
+
+ override fun newArray(size: Int) = arrayOfNulls<NoteTaskBubbleExpandBehavior>(size)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt
index ec205f87d9fa..169285f6742e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt
@@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.DebugLogger.debugLog
+import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
@@ -48,7 +49,7 @@ open class NoteTaskBubblesController
@Inject
constructor(
@Application private val context: Context,
- @Background private val bgDispatcher: CoroutineDispatcher
+ @Background private val bgDispatcher: CoroutineDispatcher,
) {
private val serviceConnector: ServiceConnector<INoteTaskBubblesService> =
@@ -57,7 +58,7 @@ constructor(
Intent(context, NoteTaskBubblesService::class.java),
Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
UserHandle.USER_SYSTEM,
- INoteTaskBubblesService.Stub::asInterface
+ INoteTaskBubblesService.Stub::asInterface,
)
/** Returns whether notes app bubble is supported. */
@@ -79,11 +80,12 @@ constructor(
open suspend fun showOrHideAppBubble(
intent: Intent,
userHandle: UserHandle,
- icon: Icon
+ icon: Icon,
+ bubbleExpandBehavior: NoteTaskBubbleExpandBehavior,
) {
withContext(bgDispatcher) {
serviceConnector
- .post { it.showOrHideAppBubble(intent, userHandle, icon) }
+ .post { it.showOrHideAppBubble(intent, userHandle, icon, bubbleExpandBehavior) }
.whenComplete { _, error ->
if (error != null) {
debugLog(error = error) {
@@ -120,16 +122,28 @@ constructor(
override fun showOrHideAppBubble(
intent: Intent,
userHandle: UserHandle,
- icon: Icon
+ icon: Icon,
+ bubbleExpandBehavior: NoteTaskBubbleExpandBehavior,
) {
mOptionalBubbles.ifPresentOrElse(
- { bubbles -> bubbles.showOrHideAppBubble(intent, userHandle, icon) },
+ { bubbles ->
+ if (
+ bubbleExpandBehavior ==
+ NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED &&
+ bubbles.isBubbleExpanded(
+ Bubble.getAppBubbleKeyForApp(intent.`package`, userHandle)
+ )
+ ) {
+ return@ifPresentOrElse
+ }
+ bubbles.showOrHideAppBubble(intent, userHandle, icon)
+ },
{
debugLog {
"Failed to show or hide bubble for intent $intent," +
"user $user, and icon $icon as bubble is empty."
}
- }
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 1fa5baaa21ae..a615963ed2ca 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -84,7 +84,7 @@ constructor(
private val userTracker: UserTracker,
private val secureSettings: SecureSettings,
@Application private val applicationScope: CoroutineScope,
- @Background private val bgCoroutineContext: CoroutineContext
+ @Background private val bgCoroutineContext: CoroutineContext,
) {
@VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>()
@@ -98,7 +98,7 @@ constructor(
if (key != Bubble.getAppBubbleKeyForApp(info.packageName, info.user)) return
// Safe guard mechanism, this callback should only be called for app bubbles.
- if (info.launchMode != NoteTaskLaunchMode.AppBubble) return
+ if (info.launchMode !is NoteTaskLaunchMode.AppBubble) return
if (isExpanding) {
debugLog { "onBubbleExpandChanged - expanding: $info" }
@@ -117,10 +117,8 @@ constructor(
} else {
getUserForHandlingNotesTaking(entryPoint)
}
- activityContext.startActivityAsUser(
- createNotesRoleHolderSettingsIntent(),
- user
- )
+
+ activityContext.startActivityAsUser(createNotesRoleHolderSettingsIntent(), user)
}
/**
@@ -140,8 +138,7 @@ constructor(
entryPoint == QUICK_AFFORDANCE -> {
userTracker.userProfiles
.firstOrNull { userManager.isManagedProfile(it.id) }
- ?.userHandle
- ?: userTracker.userHandle
+ ?.userHandle ?: userTracker.userHandle
}
// On work profile devices, SysUI always run in the main user.
else -> userTracker.userHandle
@@ -158,19 +155,14 @@ constructor(
*
* That will let users open other apps in full screen, and take contextual notes.
*/
- fun showNoteTask(
- entryPoint: NoteTaskEntryPoint,
- ) {
+ fun showNoteTask(entryPoint: NoteTaskEntryPoint) {
if (!isEnabled) return
showNoteTaskAsUser(entryPoint, getUserForHandlingNotesTaking(entryPoint))
}
/** A variant of [showNoteTask] which launches note task in the given [user]. */
- fun showNoteTaskAsUser(
- entryPoint: NoteTaskEntryPoint,
- user: UserHandle,
- ) {
+ fun showNoteTaskAsUser(entryPoint: NoteTaskEntryPoint, user: UserHandle) {
if (!isEnabled) return
applicationScope.launch("$TAG#showNoteTaskAsUser") {
@@ -178,10 +170,7 @@ constructor(
}
}
- private suspend fun awaitShowNoteTaskAsUser(
- entryPoint: NoteTaskEntryPoint,
- user: UserHandle,
- ) {
+ private suspend fun awaitShowNoteTaskAsUser(entryPoint: NoteTaskEntryPoint, user: UserHandle) {
if (!isEnabled) return
if (!noteTaskBubblesController.areBubblesAvailable()) {
@@ -222,7 +211,13 @@ constructor(
val intent = createNoteTaskIntent(info)
val icon =
Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
- noteTaskBubblesController.showOrHideAppBubble(intent, user, icon)
+ noteTaskBubblesController.showOrHideAppBubble(
+ intent,
+ user,
+ icon,
+ info.launchMode.bubbleExpandBehavior,
+ )
+
// App bubble logging happens on `onBubbleExpandChanged`.
debugLog { "onShowNoteTask - opened as app bubble: $info" }
}
@@ -399,8 +394,8 @@ constructor(
const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE = "extra_shortcut_badge_override_package"
/** Returns notes role holder settings intent. */
- fun createNotesRoleHolderSettingsIntent() = Intent(Intent.ACTION_MANAGE_DEFAULT_APP).
- putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
+ fun createNotesRoleHolderSettingsIntent() =
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP).putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 269eb870686c..8319e07525d8 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -31,6 +31,10 @@ data class NoteTaskInfo(
if (isKeyguardLocked || entryPoint == WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) {
NoteTaskLaunchMode.Activity
} else {
- NoteTaskLaunchMode.AppBubble
+ if (entryPoint == NoteTaskEntryPoint.QS_NOTES_TILE) {
+ NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.KEEP_IF_EXPANDED)
+ } else {
+ NoteTaskLaunchMode.AppBubble(NoteTaskBubbleExpandBehavior.DEFAULT)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
index 836e103f4d69..6c85f20f2bce 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
@@ -26,7 +26,8 @@ import com.android.wm.shell.bubbles.Bubbles
sealed class NoteTaskLaunchMode {
/** @see Bubbles.showOrHideAppBubble */
- object AppBubble : NoteTaskLaunchMode()
+ data class AppBubble(val bubbleExpandBehavior: NoteTaskBubbleExpandBehavior) :
+ NoteTaskLaunchMode()
/** @see Context.startActivity */
object Activity : NoteTaskLaunchMode()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 4e094cc77eae..789fdebc36eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -16,12 +16,17 @@
package com.android.systemui.qs.panels.ui.compose
-import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -39,16 +44,17 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.development.ui.compose.BuildNumber
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
-import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
-import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
+import com.android.systemui.qs.panels.ui.compose.Dimensions.FooterHeight
+import com.android.systemui.qs.panels.ui.compose.Dimensions.InterPageSpacing
import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.ui.compose.borderOnFocus
@@ -121,38 +127,78 @@ constructor(
TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
}
}
- // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
- // expected to be inside a scrollable container, this should not be an issue.
- Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) {
- PagerDots(
- pagerState = pagerState,
- activeColor = MaterialTheme.colorScheme.primary,
- nonActiveColor = MaterialTheme.colorScheme.surfaceVariant,
- modifier = Modifier.align(Alignment.Center),
- )
- CompositionLocalProvider(value = LocalContentColor provides Color.White) {
- IconButton(
- onClick = editModeStart,
- shape = RoundedCornerShape(CornerSize(28.dp)),
- modifier =
- Modifier.align(Alignment.CenterEnd)
- .borderOnFocus(
- color = MaterialTheme.colorScheme.secondary,
- cornerSize = CornerSize(FooterHeight / 2),
- ),
- ) {
- Icon(
- imageVector = Icons.Default.Edit,
- contentDescription = stringResource(id = R.string.qs_edit),
+ FooterBar(
+ buildNumberViewModelFactory = viewModel.buildNumberViewModelFactory,
+ pagerState = pagerState,
+ editModeStart = editModeStart,
+ )
+ }
+ }
+}
+
+private object Dimensions {
+ val FooterHeight = 48.dp
+ val InterPageSpacing = 16.dp
+}
+
+@Composable
+private fun FooterBar(
+ buildNumberViewModelFactory: BuildNumberViewModel.Factory,
+ pagerState: PagerState,
+ editModeStart: () -> Unit,
+) {
+ // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
+ // expected to be inside a scrollable container, this should not be an issue.
+ // Also, we construct the layout this way to do the following:
+ // * PagerDots is centered in the row, taking as much space as it needs.
+ // * On the start side, we place the BuildNumber, taking as much space as it needs, but
+ // constrained by the available space left over after PagerDots
+ // * On the end side, we place the edit mode button, with the same constraints as for
+ // BuildNumber (but it will usually fit, as it's just a square button).
+ Row(
+ modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = spacedBy(8.dp),
+ ) {
+ Row(Modifier.weight(1f)) {
+ BuildNumber(
+ viewModelFactory = buildNumberViewModelFactory,
+ textColor = MaterialTheme.colorScheme.onSurface,
+ modifier =
+ Modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ cornerSize = CornerSize(1.dp),
)
- }
+ .wrapContentSize(),
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ }
+ PagerDots(
+ pagerState = pagerState,
+ activeColor = MaterialTheme.colorScheme.primary,
+ nonActiveColor = MaterialTheme.colorScheme.surfaceVariant,
+ modifier = Modifier.wrapContentWidth(),
+ )
+ Row(Modifier.weight(1f)) {
+ Spacer(modifier = Modifier.weight(1f))
+ CompositionLocalProvider(
+ value = LocalContentColor provides MaterialTheme.colorScheme.onSurface
+ ) {
+ IconButton(
+ onClick = editModeStart,
+ shape = RoundedCornerShape(CornerSize(28.dp)),
+ modifier =
+ Modifier.borderOnFocus(
+ color = MaterialTheme.colorScheme.secondary,
+ cornerSize = CornerSize(FooterHeight / 2),
+ ),
+ ) {
+ Icon(
+ imageVector = Icons.Default.Edit,
+ contentDescription = stringResource(id = R.string.qs_edit),
+ )
}
}
}
}
-
- private object Dimensions {
- val FooterHeight = 48.dp
- val InterPageSpacing = 16.dp
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index cb57c6710553..0a80a19871fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -126,7 +126,7 @@ fun Tile(
val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
val resources = resources()
val uiState = remember(state, resources) { state.toUiState(resources) }
- val colors = TileDefaults.getColorForState(uiState)
+ val colors = TileDefaults.getColorForState(uiState, iconOnly)
val hapticsViewModel: TileHapticsViewModel? =
rememberViewModel(traceName = "TileHapticsViewModel") {
tileHapticsViewModelFactoryProvider.getHapticsViewModelFactory()?.create(tile)
@@ -365,22 +365,24 @@ private object TileDefaults {
)
@Composable
- fun getColorForState(uiState: TileUiState): TileColors {
+ fun getColorForState(uiState: TileUiState, iconOnly: Boolean): TileColors {
return when (uiState.state) {
STATE_ACTIVE -> {
- if (uiState.handlesSecondaryClick) {
+ if (uiState.handlesSecondaryClick && !iconOnly) {
activeDualTargetTileColors()
} else {
activeTileColors()
}
}
+
STATE_INACTIVE -> {
- if (uiState.handlesSecondaryClick) {
+ if (uiState.handlesSecondaryClick && !iconOnly) {
inactiveDualTargetTileColors()
} else {
inactiveTileColors()
}
}
+
else -> unavailableTileColors()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index e5607eb6e620..bff330b98fda 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
import androidx.compose.runtime.getValue
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
@@ -34,6 +35,7 @@ constructor(
columnsWithMediaViewModelFactory: QSColumnsViewModel.Factory,
paginatedGridInteractor: PaginatedGridInteractor,
inFirstPageViewModel: InFirstPageViewModel,
+ val buildNumberViewModelFactory: BuildNumberViewModel.Factory,
) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
private val hydrator = Hydrator("PaginatedGridViewModel")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 35b1b9636263..ab3862b75ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -81,7 +81,7 @@ constructor(
// additional
// guidance on how to auto add your tile
throw UnsupportedOperationException(
- "Turning on tile is not supported now"
+ "Turning on tile is not supported now. Tile spec: $tileSpec"
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 856333ea724e..aad8b4ba1191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -2707,6 +2707,44 @@ public class BubblesTest extends SysuiTestCase {
eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_openOverflow() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+
+ clearInvocations(mBubbleLogger);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(BubbleOverflow.KEY, 0);
+ verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_SELECTED);
+ verifyNoMoreInteractions(mBubbleLogger);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_fromOverflowToBar() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ Bubble overflowBubble = mBubbleData.getOverflowBubbleWithKey(mRow.getKey());
+ assertThat(overflowBubble).isNotNull();
+
+ // Promote overflow bubble and check that it is logged
+ mBubbleController.promoteBubbleFromOverflow(overflowBubble);
+ verify(mBubbleLogger).log(eqBubbleWithKey(overflowBubble.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR));
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt
new file mode 100644
index 000000000000..379c00842b62
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/content/ClipboardManagerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.clipboardManager by Kosmos.Fixture { mock<ClipboardManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt
new file mode 100644
index 000000000000..3ce119576096
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/development/data/repository/DevelopmentSettingRepositoryKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.data.repository
+
+import android.os.userManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.settings.fakeGlobalSettings
+
+val Kosmos.developmentSettingRepository by
+ Kosmos.Fixture { DevelopmentSettingRepository(fakeGlobalSettings, userManager, testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt
new file mode 100644
index 000000000000..aa4dd18a6cba
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/development/domain/interactor/BuildNumberInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.domain.interactor
+
+import android.content.clipboardManager
+import android.content.res.mainResources
+import com.android.systemui.development.data.repository.developmentSettingRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.buildNumberInteractor by
+ Kosmos.Fixture {
+ BuildNumberInteractor(
+ developmentSettingRepository,
+ mainResources,
+ userRepository,
+ { clipboardManager },
+ testDispatcher,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt
new file mode 100644
index 000000000000..c827311a6ac3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/development/ui/viewmodel/BuildNumberViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.development.ui.viewmodel
+
+import com.android.systemui.development.domain.interactor.buildNumberInteractor
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.buildNumberViewModelFactory by
+ Kosmos.Fixture {
+ object : BuildNumberViewModel.Factory {
+ override fun create(): BuildNumberViewModel {
+ return BuildNumberViewModel(buildNumberInteractor)
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 0e5edb75846d..2e80293eafff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import com.android.systemui.development.ui.viewmodel.buildNumberViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
@@ -26,5 +27,6 @@ val Kosmos.paginatedGridViewModel by
qsColumnsViewModelFactory,
paginatedGridInteractor,
inFirstPageViewModel,
+ buildNumberViewModelFactory,
)
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 619c8e30c78e..7ca9239d2062 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -142,34 +142,32 @@ public final class RavenwoodConfig {
}
/**
- * Configure the given system property as immutable for the duration of the test.
- * Read access to the key is allowed, and write access will fail. When {@code value} is
- * {@code null}, the value is left as undefined.
- *
- * All properties in the {@code debug.*} namespace are automatically mutable, with no
- * developer action required.
- *
- * Has no effect on non-Ravenwood environments.
+ * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyImmutable(String, Object)}
*/
+ @Deprecated
public Builder setSystemPropertyImmutable(@NonNull String key,
@Nullable Object value) {
- mConfig.mSystemProperties.setValue(key, value);
- mConfig.mSystemProperties.setAccessReadOnly(key);
return this;
}
/**
- * Configure the given system property as mutable for the duration of the test.
- * Both read and write access to the key is allowed, and its value will be reset between
- * each test. When {@code value} is {@code null}, the value is left as undefined.
- *
- * All properties in the {@code debug.*} namespace are automatically mutable, with no
- * developer action required.
- *
- * Has no effect on non-Ravenwood environments.
+ * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyMutable(String, Object)}
*/
+ @Deprecated
public Builder setSystemPropertyMutable(@NonNull String key,
@Nullable Object value) {
+ return this;
+ }
+
+ Builder setSystemPropertyImmutableReal(@NonNull String key,
+ @Nullable Object value) {
+ mConfig.mSystemProperties.setValue(key, value);
+ mConfig.mSystemProperties.setAccessReadOnly(key);
+ return this;
+ }
+
+ Builder setSystemPropertyMutableReal(@NonNull String key,
+ @Nullable Object value) {
mConfig.mSystemProperties.setValue(key, value);
mConfig.mSystemProperties.setAccessReadWrite(key);
return this;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index f7acd9022300..5681a9040f63 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -152,7 +152,7 @@ public final class RavenwoodRule implements TestRule {
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyImmutable(key, value);
+ mBuilder.setSystemPropertyImmutableReal(key, value);
return this;
}
@@ -167,7 +167,7 @@ public final class RavenwoodRule implements TestRule {
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyMutable(key, value);
+ mBuilder.setSystemPropertyMutableReal(key, value);
return this;
}
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index dbeca82ade89..2d3782fb3181 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -16,6 +16,8 @@
package com.android.server.companion.securechannel;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNKNOWN;
+
import android.annotation.NonNull;
import android.content.Context;
import android.os.Build;
@@ -67,7 +69,7 @@ public class SecureChannel {
private D2DConnectionContextV1 mConnectionContext;
private String mAlias;
- private int mVerificationResult;
+ private int mVerificationResult = FLAG_FAILURE_UNKNOWN;
private boolean mPskVerified;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 679c7ac3ceac..3ce645158fe4 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -16,6 +16,10 @@
package com.android.server.accounts;
+import static android.Manifest.permission.COPY_ACCOUNTS;
+import static android.Manifest.permission.REMOVE_ACCOUNTS;
+import static android.app.admin.flags.Flags.splitCreateManagedProfileEnabled;
+
import android.Manifest;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
@@ -1739,9 +1743,11 @@ public class AccountManagerService
public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
final int userFrom, int userTo) {
int callingUid = Binder.getCallingUid();
- if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
+ if (isCrossUser(callingUid, UserHandle.USER_ALL)
+ && !hasCopyAccountsPermission()) {
throw new SecurityException("Calling copyAccountToUser requires "
- + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ + " or " + COPY_ACCOUNTS);
}
final UserAccounts fromAccounts = getUserAccounts(userFrom);
final UserAccounts toAccounts = getUserAccounts(userTo);
@@ -1793,6 +1799,12 @@ public class AccountManagerService
}
}
+ private boolean hasCopyAccountsPermission() {
+ return splitCreateManagedProfileEnabled()
+ && mContext.checkCallingOrSelfPermission(COPY_ACCOUNTS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public boolean accountAuthenticated(final Account account) {
final int callingUid = Binder.getCallingUid();
@@ -2346,7 +2358,8 @@ public class AccountManagerService
UserHandle user = UserHandle.of(userId);
if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
&& !isSystemUid(callingUid)
- && !isProfileOwner(callingUid)) {
+ && !isProfileOwner(callingUid)
+ && !hasRemoveAccountsPermission()) {
String msg = String.format(
"uid %s cannot remove accounts of type: %s",
callingUid,
@@ -2408,6 +2421,12 @@ public class AccountManagerService
}
}
+ private boolean hasRemoveAccountsPermission() {
+ return splitCreateManagedProfileEnabled()
+ && mContext.checkCallingOrSelfPermission(REMOVE_ACCOUNTS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public boolean removeAccountExplicitly(Account account) {
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index ef5296eef492..78c4f74f3afa 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -257,6 +257,7 @@ public class SettingsToPropertiesMapper {
"wear_systems",
"wear_sysui",
"wear_system_managed_surfaces",
+ "wear_watchfaces",
"window_surfaces",
"windowing_frontend",
"xr",
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d8483f721306..b7af9a4b17bd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1310,7 +1310,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
// Do not reset the default (current) IME when it is a 3rd-party IME
String selectedMethodId = bindingController.getSelectedMethodId();
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- if (selectedMethodId != null
+ if (selectedMethodId != null && settings.getMethodMap().get(selectedMethodId) != null
&& !settings.getMethodMap().get(selectedMethodId).isSystem()) {
return;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 946e89604553..7c9d9c57a8b0 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -326,8 +326,15 @@ public class ContextHubService extends IContextHubService.Stub {
}
if (Flags.offloadApi()) {
- mHubInfoRegistry = new HubInfoRegistry(mContextHubWrapper);
- Log.i(TAG, "Enabling generic offload API");
+ HubInfoRegistry registry;
+ try {
+ registry = new HubInfoRegistry(mContextHubWrapper);
+ Log.i(TAG, "Enabling generic offload API");
+ } catch (UnsupportedOperationException e) {
+ registry = null;
+ Log.w(TAG, "Generic offload API not supported, disabling");
+ }
+ mHubInfoRegistry = registry;
} else {
mHubInfoRegistry = null;
Log.i(TAG, "Disabling generic offload API");
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 00bab8af44f3..8495b6c403bf 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -28,6 +28,7 @@ import android.media.quality.ISoundProfileCallback;
import android.media.quality.MediaQualityContract;
import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
+import android.media.quality.PictureProfileHandle;
import android.media.quality.SoundProfile;
import android.os.PersistableBundle;
import android.util.Log;
@@ -249,6 +250,11 @@ public class MediaQualityService extends SystemService {
}
@Override
+ public PictureProfileHandle getPictureProfileHandle(String id) {
+ return null;
+ }
+
+ @Override
public SoundProfile createSoundProfile(SoundProfile pp) {
// TODO: implement
return pp;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 4857b02eaf7c..70a8f563275f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -344,6 +344,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
private ActivityRecord mTopResumedActivity;
/**
+ * Cached value of the topmost resumed activity that reported to the client.
+ */
+ private ActivityRecord mLastReportedTopResumedActivity;
+
+ /**
* Flag indicating whether we're currently waiting for the previous top activity to handle the
* loss of the state and report back before making new activity top resumed.
*/
@@ -2287,15 +2292,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
* sent to the new top resumed activity.
*/
ActivityRecord updateTopResumedActivityIfNeeded(String reason) {
- if (!readyToResume()) {
- return mTopResumedActivity;
- }
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) {
if (topRootTask == null) {
// There's no focused task and there won't have any resumed activity either.
scheduleTopResumedActivityStateLossIfNeeded();
+ mTopResumedActivity = null;
}
if (mService.isSleepingLocked()) {
// There won't be a next resumed activity. The top process should still be updated
@@ -2339,25 +2342,27 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
/** Schedule current top resumed activity state loss */
private void scheduleTopResumedActivityStateLossIfNeeded() {
- if (mTopResumedActivity == null) {
+ if (mLastReportedTopResumedActivity == null) {
return;
}
// mTopResumedActivityWaitingForPrev == true at this point would mean that an activity
// before the prevTopActivity one hasn't reported back yet. So server never sent the top
// resumed state change message to prevTopActivity.
- if (!mTopResumedActivityWaitingForPrev
- && mTopResumedActivity.scheduleTopResumedActivityChanged(false /* onTop */)) {
- scheduleTopResumedStateLossTimeout(mTopResumedActivity);
+ if (!mTopResumedActivityWaitingForPrev && readyToResume()
+ && mLastReportedTopResumedActivity.scheduleTopResumedActivityChanged(
+ false /* onTop */)) {
+ scheduleTopResumedStateLossTimeout(mLastReportedTopResumedActivity);
mTopResumedActivityWaitingForPrev = true;
+ mLastReportedTopResumedActivity = null;
}
- mTopResumedActivity = null;
}
/** Schedule top resumed state change if previous top activity already reported back. */
private void scheduleTopResumedActivityStateIfNeeded() {
- if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) {
+ if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev && readyToResume()) {
mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */);
+ mLastReportedTopResumedActivity = mTopResumedActivity;
}
}
@@ -2611,6 +2616,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
*/
void endDeferResume() {
mDeferResumeCount--;
+ if (readyToResume() && mLastReportedTopResumedActivity != null
+ && mTopResumedActivity != mLastReportedTopResumedActivity) {
+ scheduleTopResumedActivityStateLossIfNeeded();
+ }
}
/** @return True if resume can be called. */
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 81a04af17703..f0e12fec3107 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -2025,22 +2025,9 @@ class RecentTasks {
// Fill in some deprecated values.
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
- rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
if (!getTasksAllowed) {
Task.trimIneffectiveInfo(tr, rti);
}
-
- // Fill in organized child task info for the task created by organizer.
- if (tr.mCreatedByOrganizer) {
- for (int i = tr.getChildCount() - 1; i >= 0; i--) {
- final Task childTask = tr.getChildAt(i).asTask();
- if (childTask != null && childTask.isOrganized()) {
- final ActivityManager.RecentTaskInfo cti = new ActivityManager.RecentTaskInfo();
- childTask.fillTaskInfo(cti, true /* stripExtras */, tda);
- rti.childrenTaskInfos.add(cti);
- }
- }
- }
return rti;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e8ae28008c89..9504fc7d4d30 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -128,7 +128,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -256,9 +255,6 @@ class Task extends TaskFragment {
private static final String ATTR_MIN_HEIGHT = "min_height";
private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
- private static final String ATTR_LAST_SNAPSHOT_TASK_SIZE = "last_snapshot_task_size";
- private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets";
- private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size";
// How long to wait for all background Activities to redraw following a call to
// convertToTranslucent().
@@ -472,10 +468,6 @@ class Task extends TaskFragment {
// NOTE: This value needs to be persisted with each task
private TaskDescription mTaskDescription;
- // Information about the last snapshot that should be persisted with the task to allow SystemUI
- // to layout without loading all the task snapshots
- final PersistedTaskSnapshotData mLastTaskSnapshotData;
-
/** @see #setCanAffectSystemUiFlags */
private boolean mCanAffectSystemUiFlags = true;
@@ -635,14 +627,13 @@ class Task extends TaskFragment {
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
boolean _autoRemoveRecents, int _userId, int _effectiveUid,
String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
- TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData,
- int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid,
- String callingPackage, @Nullable String callingFeatureId, int resizeMode,
- boolean supportsPictureInPicture, boolean _realActivitySuspended,
- boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
- IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
- boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
- boolean _removeWithTaskOrganizer) {
+ TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
+ int nextTaskId, int callingUid, String callingPackage,
+ @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
+ boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
+ ActivityInfo info, IVoiceInteractionSession _voiceSession,
+ IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, IBinder _launchCookie,
+ boolean _deferTaskAppear, boolean _removeWithTaskOrganizer) {
super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
mTaskId = _taskId;
@@ -652,9 +643,6 @@ class Task extends TaskFragment {
mTaskDescription = _lastTaskDescription != null
? _lastTaskDescription
: new TaskDescription();
- mLastTaskSnapshotData = _lastSnapshotData != null
- ? _lastSnapshotData
- : new PersistedTaskSnapshotData();
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -3111,7 +3099,6 @@ class Task extends TaskFragment {
}
void onSnapshotChanged(TaskSnapshot snapshot) {
- mLastTaskSnapshotData.set(snapshot);
mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(
mTaskId, snapshot);
}
@@ -3990,19 +3977,6 @@ class Task extends TaskFragment {
out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight);
out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION);
- if (mLastTaskSnapshotData.taskSize != null) {
- out.attribute(null, ATTR_LAST_SNAPSHOT_TASK_SIZE,
- mLastTaskSnapshotData.taskSize.flattenToString());
- }
- if (mLastTaskSnapshotData.contentInsets != null) {
- out.attribute(null, ATTR_LAST_SNAPSHOT_CONTENT_INSETS,
- mLastTaskSnapshotData.contentInsets.flattenToString());
- }
- if (mLastTaskSnapshotData.bufferSize != null) {
- out.attribute(null, ATTR_LAST_SNAPSHOT_BUFFER_SIZE,
- mLastTaskSnapshotData.bufferSize.flattenToString());
- }
-
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
affinityIntent.saveToXml(out);
@@ -4069,7 +4043,6 @@ class Task extends TaskFragment {
int taskId = INVALID_TASK_ID;
final int outerDepth = in.getDepth();
TaskDescription taskDescription = new TaskDescription();
- PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
int taskAffiliation = INVALID_TASK_ID;
int prevTaskId = INVALID_TASK_ID;
int nextTaskId = INVALID_TASK_ID;
@@ -4176,15 +4149,6 @@ class Task extends TaskFragment {
case ATTR_PERSIST_TASK_VERSION:
persistTaskVersion = Integer.parseInt(attrValue);
break;
- case ATTR_LAST_SNAPSHOT_TASK_SIZE:
- lastSnapshotData.taskSize = Point.unflattenFromString(attrValue);
- break;
- case ATTR_LAST_SNAPSHOT_CONTENT_INSETS:
- lastSnapshotData.contentInsets = Rect.unflattenFromString(attrValue);
- break;
- case ATTR_LAST_SNAPSHOT_BUFFER_SIZE:
- lastSnapshotData.bufferSize = Point.unflattenFromString(attrValue);
- break;
default:
if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
Slog.w(TAG, "Task: Unknown attribute=" + attrName);
@@ -4278,7 +4242,6 @@ class Task extends TaskFragment {
.setLastTimeMoved(lastTimeOnTop)
.setNeverRelinquishIdentity(neverRelinquishIdentity)
.setLastTaskDescription(taskDescription)
- .setLastSnapshotData(lastSnapshotData)
.setTaskAffiliation(taskAffiliation)
.setPrevAffiliateTaskId(prevTaskId)
.setNextAffiliateTaskId(nextTaskId)
@@ -6444,7 +6407,6 @@ class Task extends TaskFragment {
private long mLastTimeMoved;
private boolean mNeverRelinquishIdentity;
private TaskDescription mLastTaskDescription;
- private PersistedTaskSnapshotData mLastSnapshotData;
private int mTaskAffiliation;
private int mPrevAffiliateTaskId = INVALID_TASK_ID;
private int mNextAffiliateTaskId = INVALID_TASK_ID;
@@ -6672,11 +6634,6 @@ class Task extends TaskFragment {
return this;
}
- private Builder setLastSnapshotData(PersistedTaskSnapshotData lastSnapshotData) {
- mLastSnapshotData = lastSnapshotData;
- return this;
- }
-
private Builder setOrigActivity(ComponentName origActivity) {
mOrigActivity = origActivity;
return this;
@@ -6825,7 +6782,7 @@ class Task extends TaskFragment {
return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
- mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,
+ mNeverRelinquishIdentity, mLastTaskDescription,
mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,
mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 091896590b6b..c42aa37d847b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -788,9 +788,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
deferResume = false;
// Already calls ensureActivityConfig
mService.mRootWindowContainer.ensureActivitiesVisible();
- if (!mService.mRootWindowContainer.resumeFocusedTasksTopActivities()) {
- mService.mTaskSupervisor.updateTopResumedActivityIfNeeded("endWCT-effects");
- }
+ mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
haveConfigChanges.valueAt(i).forAllActivities(r -> {
@@ -816,10 +814,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
if (deferResume) {
mService.mTaskSupervisor.endDeferResume();
- // Transient launching the Recents via HIERARCHY_OP_TYPE_PENDING_INTENT directly
- // resume the Recents activity with no TRANSACT_EFFECTS_LIFECYCLE. Explicitly
- // checks if the top resumed activity should be updated after defer-resume ended.
- mService.mTaskSupervisor.updateTopResumedActivityIfNeeded("endWCT");
}
mService.continueWindowLayout();
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 5bb6b19cd63e..d08715586580 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -194,7 +194,13 @@ public class InputMethodServiceTest {
() -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(),
true /* expected */,
false /* inputViewStarted */);
- assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ // The IME visibility is only sent at the end of the animation. Therefore, we have to
+ // wait until the visibility was sent to the server and the IME window hidden.
+ eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+ } else {
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ }
}
/**
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
index 0d5d277b00ef..ed927c6ab699 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
@@ -26,8 +26,7 @@ import android.content.Context;
import android.os.Process;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodConfig.Config;
+import android.platform.test.ravenwood.RavenwoodRule;
import com.android.internal.os.PowerStats;
import com.android.server.power.feature.flags.Flags;
@@ -39,10 +38,9 @@ import org.junit.Test;
public class WakelockPowerStatsCollectorTest {
- @Config
- public static final RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder()
- .setProvideMainThread(true)
+ @Rule
+ public final RavenwoodRule mRule =
+ new RavenwoodRule.Builder()
.setSystemPropertyImmutable(
"persist.sys.com.android.server.power.feature.flags."
+ "framework_wakelock_info-override",
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 704c1b858b8d..e6b4bc98ea4c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -17178,8 +17178,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_granted() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// qualifying posted notification
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17254,8 +17252,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_granted_onlyNotifiesOnce() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// qualifying posted notification
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17285,8 +17281,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_revoked() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// start from true state
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
@@ -17350,8 +17344,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_revoked_onlyNotifiesOnce() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
// start from true state
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
@@ -17387,8 +17379,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testPostPromotableNotification() throws Exception {
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
assertThat(mBinderService.appCanBePromoted(mPkg, mUid)).isTrue();
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -17415,8 +17405,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testPostPromotableNotification_noPermission() throws Exception {
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
@@ -17444,8 +17432,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testPostPromotableNotification_unimportantNotification() throws Exception {
mBinderService.setCanBePromoted(mPkg, mUid, true, true);
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
Notification n = new Notification.Builder(mContext, mMinChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 7f260f85a755..70f57eb40385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -357,6 +357,25 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
assertEquals(activity1.app, mAtm.mTopApp);
}
+ @Test
+ public void testTopResumedActivity_deferResume() {
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity2.setState(ActivityRecord.State.RESUMED, "test");
+ assertEquals(activity2.app, mAtm.mTopApp);
+ reset(activity2);
+
+ // Verify that no top-resumed activity changes to the client while defer-resume enabled.
+ mSupervisor.beginDeferResume();
+ activity1.getTask().moveToFront("test");
+ activity1.setState(ActivityRecord.State.RESUMED, "test");
+ verify(activity2, never()).scheduleTopResumedActivityChanged(eq(false));
+
+ // Verify that the change is scheduled to the client after defer-resumed disabled
+ mSupervisor.endDeferResume();
+ verify(activity2).scheduleTopResumedActivityChanged(eq(false));
+ }
+
/**
* We need to launch home again after user unlocked for those displays that do not have
* encryption aware home app.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index df17cd1d24b7..7ed8283efffd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -496,24 +496,6 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- public void testAppendOrganizedChildTaskInfo() {
- final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build();
- root.mCreatedByOrganizer = true;
- // Add organized and non-organized child.
- final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build();
- final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build();
- doReturn(true).when(child1).isOrganized();
- doReturn(false).when(child2).isOrganized();
- mRecentTasks.add(root);
-
- // Make sure only organized child will be appended.
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final List<RecentTaskInfo> childrenTaskInfos = infos.get(0).childrenTaskInfos;
- assertEquals(childrenTaskInfos.size(), 1);
- assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId);
- }
-
- @Test
public void testAddTasksHomeClearUntrackedTasks_expectFinish() {
// There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK |
// FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed
@@ -1420,46 +1402,6 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
- public void testLastSnapshotData_snapshotSaved() {
- final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80));
- final Task task1 = createTaskBuilder(".Task").build();
- task1.onSnapshotChanged(snapshot);
-
- mRecentTasks.add(task1);
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- infos.get(0).lastSnapshotData;
- assertTrue(lastSnapshotData.taskSize.equals(100, 100));
- assertTrue(lastSnapshotData.bufferSize.equals(80, 80));
- }
-
- @Test
- public void testLastSnapshotData_noBuffer() {
- final Task task1 = createTaskBuilder(".Task").build();
- final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null);
- task1.onSnapshotChanged(snapshot);
-
- mRecentTasks.add(task1);
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- infos.get(0).lastSnapshotData;
- assertTrue(lastSnapshotData.taskSize.equals(100, 100));
- assertNull(lastSnapshotData.bufferSize);
- }
-
- @Test
- public void testLastSnapshotData_notSet() {
- final Task task1 = createTaskBuilder(".Task").build();
-
- mRecentTasks.add(task1);
- final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
- final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- infos.get(0).lastSnapshotData;
- assertNull(lastSnapshotData.taskSize);
- assertNull(lastSnapshotData.bufferSize);
- }
-
- @Test
public void testCreateRecentTaskInfo_detachedTask() {
final Task task = createTaskBuilder(".Task").build();
final ComponentName componentName = getUniqueComponentName();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6490cbe3e31a..7cfdec664a92 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9734,6 +9734,35 @@ public class CarrierConfigManager {
"carrier_supported_satellite_services_per_provider_bundle";
/**
+ * A PersistableBundle that contains a list of key-value pairs, where the values are integer
+ * arrays.
+ * <p>
+ * Keys are the IDs of regional satellite configs as strings and values are
+ * integer arrays of earfcns in the corresponding regions.
+ *
+ * An example config for two regions "1" and "2":
+ * <pre>{@code
+ * <carrier_config>
+ * <pbundle_as_map name="regional_satellite_earfcn_bundle">
+ * <int-array name = "1" num = "2">
+ * <item value = "100"/>
+ * <item value = "200"/>
+ * </int-array>
+ * <int-array name = "2" num = "1">
+ * <item value = "200"/>
+ * </int-array>
+ * </pbundle_as_map>
+ * </carrier_config>
+ * }</pre>
+ * <p>
+ * This config is empty by default.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE =
+ "regional_satellite_earfcn_bundle";
+
+ /**
* This config enables modem to scan satellite PLMNs specified as per
* {@link #KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE} and attach to same
* in case cellular networks are not enabled. This will need specific agreement between
@@ -11264,6 +11293,9 @@ public class CarrierConfigManager {
sDefaults.putPersistableBundle(
KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
PersistableBundle.EMPTY);
+ sDefaults.putPersistableBundle(
+ KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE,
+ PersistableBundle.EMPTY);
sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 180);
sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
index 2cd625eec032..4d495adf727b 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
@@ -18,7 +18,9 @@ package android.app.jank.tests;
import static org.junit.Assert.assertEquals;
+import android.app.jank.AppJankStats;
import android.app.jank.Flags;
+import android.app.jank.FrameOverrunHistogram;
import android.app.jank.JankDataProcessor;
import android.app.jank.StateTracker;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -39,6 +41,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -154,6 +157,73 @@ public class JankDataProcessorTest {
assertEquals(totalFrames, histogramFrames);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void mergeAppJankStats_confirmStatAddedToPendingStats() {
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ mJankDataProcessor.getPendingJankStats();
+
+ assertEquals(pendingStats.size(), 0);
+
+ AppJankStats jankStats = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+
+ pendingStats = mJankDataProcessor.getPendingJankStats();
+
+ assertEquals(pendingStats.size(), 1);
+ }
+
+ /**
+ * This test confirms matching states are combined into one pending stat. When JankStats are
+ * merged from outside the platform they will contain widget category, widget id and widget
+ * state. If an incoming JankStats matches a pending stat on all those fields the incoming
+ * JankStat will be merged into the existing stat.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void mergeAppJankStats_confirmStatsWithMatchingStatesAreCombinedIntoOnePendingStat() {
+ AppJankStats jankStats = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ mJankDataProcessor.getPendingJankStats();
+ assertEquals(pendingStats.size(), 1);
+
+ AppJankStats secondJankStat = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(secondJankStat, sActivityName);
+
+ pendingStats = mJankDataProcessor.getPendingJankStats();
+
+ assertEquals(pendingStats.size(), 1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void mergeAppJankStats_whenStatsWithMatchingStatesMerge_confirmFrameCountsAdded() {
+ AppJankStats jankStats = getAppJankStats();
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+ mJankDataProcessor.mergeJankStats(jankStats, sActivityName);
+
+ HashMap<String, JankDataProcessor.PendingJankStat> pendingStats =
+ mJankDataProcessor.getPendingJankStats();
+
+ String statKey = pendingStats.keySet().iterator().next();
+ JankDataProcessor.PendingJankStat pendingStat = pendingStats.get(statKey);
+
+ assertEquals(pendingStats.size(), 1);
+ // The same jankStats objects are merged twice, this should result in the frame counts being
+ // doubled.
+ assertEquals(jankStats.getJankyFrameCount() * 2, pendingStat.getJankyFrames());
+ assertEquals(jankStats.getTotalFrameCount() * 2, pendingStat.getTotalFrames());
+
+ int[] originalHistogramBuckets = jankStats.getFrameOverrunHistogram().getBucketCounters();
+ int[] frameOverrunBuckets = pendingStat.getFrameOverrunBuckets();
+
+ for (int i = 0; i < frameOverrunBuckets.length; i++) {
+ assertEquals(originalHistogramBuckets[i] * 2, frameOverrunBuckets[i]);
+ }
+ }
+
// TODO b/375005277 add tests that cover logging and releasing resources back to pool.
private long getTotalFramesCounted() {
@@ -276,4 +346,26 @@ public class JankDataProcessorTest {
return mockData;
}
+ private AppJankStats getAppJankStats() {
+ AppJankStats jankStats = new AppJankStats(
+ /*App Uid*/APP_ID,
+ /*Widget Id*/"test widget id",
+ /*Widget Category*/AppJankStats.SCROLL,
+ /*Widget State*/AppJankStats.SCROLLING,
+ /*Total Frames*/100,
+ /*Janky Frames*/25,
+ getOverrunHistogram()
+ );
+ return jankStats;
+ }
+
+ private FrameOverrunHistogram getOverrunHistogram() {
+ FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram();
+ overrunHistogram.addFrameOverrunMillis(-2);
+ overrunHistogram.addFrameOverrunMillis(1);
+ overrunHistogram.addFrameOverrunMillis(5);
+ overrunHistogram.addFrameOverrunMillis(25);
+ return overrunHistogram;
+ }
+
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt
index 69fde0168b14..9e488486e16a 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/StartMediaProjectionAppHelper.kt
@@ -65,10 +65,45 @@ constructor(
.waitForAndVerify()
}
+ fun startSingleAppMediaProjectionWithExtraIntent(
+ wmHelper: WindowManagerStateHelper,
+ targetApp: StandardAppHelper
+ ) {
+ clickStartMediaProjectionWithExtraIntentButton()
+ chooseSingleAppOption()
+ startScreenSharing()
+ selectTargetApp(targetApp.appName)
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+
+ fun startSingleAppMediaProjectionFromRecents(
+ wmHelper: WindowManagerStateHelper,
+ targetApp: StandardAppHelper,
+ recentTasksIndex: Int = 0,
+ ) {
+ clickStartMediaProjectionButton()
+ chooseSingleAppOption()
+ startScreenSharing()
+ selectTargetAppRecent(recentTasksIndex)
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(targetApp)
+ .waitForAndVerify()
+ }
+
private fun clickStartMediaProjectionButton() {
findObject(By.res(packageName, START_MEDIA_PROJECTION_BUTTON_ID)).also { it.click() }
}
+ private fun clickStartMediaProjectionWithExtraIntentButton() {
+ findObject(By.res(packageName, START_MEDIA_PROJECTION_NEW_INTENT_BUTTON_ID)).also { it.click() }
+ }
+
private fun chooseEntireScreenOption() {
findObject(By.res(SCREEN_SHARE_OPTIONS_PATTERN)).also { it.click() }
@@ -92,6 +127,13 @@ constructor(
findObject(By.text(targetAppName)).also { it.click() }
}
+ private fun selectTargetAppRecent(recentTasksIndex: Int) {
+ // Scroll to to find target app to launch then click app icon it to start capture
+ val recentsTasksRecycler =
+ findObject(By.res(SYSTEMUI_PACKAGE, MEDIA_PROJECTION_RECENT_TASKS))
+ recentsTasksRecycler.children[recentTasksIndex].also{ it.click() }
+ }
+
private fun chooseSingleAppOption() {
findObject(By.res(SCREEN_SHARE_OPTIONS_PATTERN)).also { it.click() }
@@ -116,8 +158,10 @@ constructor(
const val TIMEOUT: Long = 5000L
const val ACCEPT_RESOURCE_ID: String = "android:id/button1"
const val START_MEDIA_PROJECTION_BUTTON_ID: String = "button_start_mp"
+ const val START_MEDIA_PROJECTION_NEW_INTENT_BUTTON_ID: String = "button_start_mp_new_intent"
val SCREEN_SHARE_OPTIONS_PATTERN: Pattern =
Pattern.compile("$SYSTEMUI_PACKAGE:id/screen_share_mode_(options|spinner)")
+ const val MEDIA_PROJECTION_RECENT_TASKS: String = "media_projection_recent_tasks_recycler"
const val ENTIRE_SCREEN_STRING_RES_NAME: String =
"screen_share_permission_dialog_option_entire_screen"
const val SINGLE_APP_STRING_RES_NAME: String =
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
index 46f01e6c9752..c34d2003ef42 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
@@ -16,17 +16,27 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
android:orientation="vertical"
android:background="@android:color/holo_orange_light">
<Button
android:id="@+id/button_start_mp"
- android:layout_width="500dp"
- android:layout_height="500dp"
+ android:layout_margin="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="Start Media Projection"
android:textAppearance="?android:attr/textAppearanceLarge"/>
+ <Button
+ android:id="@+id/button_start_mp_new_intent"
+ android:layout_margin="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="Start Media Projection with extra intent"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java
index a24a48269d7c..b29b87450197 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/StartMediaProjectionActivity.java
@@ -19,7 +19,8 @@ package com.android.server.wm.flicker.testapp;
import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.EXTRA_MESSENGER;
import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.MSG_SERVICE_DESTROYED;
import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.MSG_START_FOREGROUND_DONE;
-import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.REQUEST_CODE;
+import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.REQUEST_CODE_NORMAL;
+import static com.android.wm.shell.flicker.utils.MediaProjectionUtils.REQUEST_CODE_EXTRA_INTENT;
import android.app.Activity;
import android.content.ComponentName;
@@ -71,13 +72,17 @@ public class StartMediaProjectionActivity extends Activity {
setContentView(R.layout.activity_start_media_projection);
Button startMediaProjectionButton = findViewById(R.id.button_start_mp);
+ Button startMediaProjectionButton2 = findViewById(R.id.button_start_mp_new_intent);
startMediaProjectionButton.setOnClickListener(v ->
- startActivityForResult(mService.createScreenCaptureIntent(), REQUEST_CODE));
+ startActivityForResult(mService.createScreenCaptureIntent(), REQUEST_CODE_NORMAL));
+ startMediaProjectionButton2.setOnClickListener(v ->
+ startActivityForResult(mService.createScreenCaptureIntent(),
+ REQUEST_CODE_EXTRA_INTENT));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode != REQUEST_CODE) {
+ if (requestCode != REQUEST_CODE_NORMAL && requestCode != REQUEST_CODE_EXTRA_INTENT) {
throw new IllegalStateException("Unknown request code: " + requestCode);
}
if (resultCode != RESULT_OK) {
@@ -85,6 +90,11 @@ public class StartMediaProjectionActivity extends Activity {
}
Log.d(TAG, "onActivityResult");
startMediaProjectionService(resultCode, data);
+ if (requestCode == REQUEST_CODE_EXTRA_INTENT) {
+ Intent startMain = new Intent(Intent.ACTION_MAIN);
+ startMain.addCategory(Intent.CATEGORY_HOME);
+ startActivity(startMain);
+ }
}
private void startMediaProjectionService(int resultCode, Intent resultData) {
@@ -122,7 +132,7 @@ public class StartMediaProjectionActivity extends Activity {
displayBounds.width(), displayBounds.height(), PixelFormat.RGBA_8888, 1);
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
- "DanielDisplay",
+ "TestDisplay",
displayBounds.width(),
displayBounds.height(),
DisplayMetrics.DENSITY_HIGH,