summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/OWNERS1
-rw-r--r--cmds/incident_helper/OWNERS1
-rw-r--r--core/java/android/app/assist/AssistStructure.java13
-rw-r--r--core/java/com/android/internal/compat/Android.bp1
-rw-r--r--core/java/com/android/internal/compat/compat_logging_flags.aconfig3
-rw-r--r--core/java/com/android/internal/foldables/Android.bp1
-rw-r--r--core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig1
-rw-r--r--core/jni/android_text_Hyphenator.cpp22
-rw-r--r--core/proto/OWNERS1
-rw-r--r--graphics/java/Android.bp1
-rw-r--r--graphics/java/android/framework_graphics.aconfig3
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt80
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java182
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/aconfig/hwui_flags.aconfig1
-rw-r--r--media/java/android/media/FadeManagerConfiguration.java16
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig17
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java42
-rw-r--r--native/android/surface_control.cpp4
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java82
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/RescueParty.java4
-rw-r--r--packages/CrashRecovery/services/java/com/android/utils/ArrayUtils.java115
-rw-r--r--packages/CrashRecovery/services/java/com/android/utils/BackgroundThread.java103
-rw-r--r--packages/CrashRecovery/services/java/com/android/utils/FileUtils.java128
-rw-r--r--packages/CrashRecovery/services/java/com/android/utils/HandlerExecutor.java46
-rw-r--r--packages/CrashRecovery/services/java/com/android/utils/LongArrayQueue.java188
-rw-r--r--packages/CrashRecovery/services/java/com/android/utils/XmlUtils.java118
-rw-r--r--packages/PackageInstaller/Android.bp3
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml11
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java173
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java59
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java36
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java327
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java100
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java202
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java621
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java133
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt121
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt28
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml1
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java121
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt)45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt)4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt2
-rw-r--r--ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java47
-rw-r--r--ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java39
-rw-r--r--ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java43
-rw-r--r--ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java2
-rw-r--r--ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java6
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java26
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java13
-rw-r--r--services/backup/Android.bp1
-rw-r--r--services/backup/flags.aconfig3
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java6
-rw-r--r--services/core/java/com/android/server/am/Android.bp1
-rw-r--r--services/core/java/com/android/server/am/OWNERS1
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig1
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java11
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java12
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java7
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java15
-rw-r--r--services/core/java/com/android/server/biometrics/Android.bp1
-rw-r--r--services/core/java/com/android/server/biometrics/biometrics.aconfig1
-rw-r--r--services/core/java/com/android/server/display/feature/Android.bp1
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig1
-rw-r--r--services/core/java/com/android/server/feature/Android.bp1
-rw-r--r--services/core/java/com/android/server/feature/dropbox_flags.aconfig1
-rw-r--r--services/core/java/com/android/server/media/AudioManagerRouteController.java59
-rw-r--r--services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java75
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java18
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java14
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java8
-rw-r--r--services/core/java/com/android/server/net/Android.bp1
-rw-r--r--services/core/java/com/android/server/net/flags.aconfig1
-rw-r--r--services/core/java/com/android/server/notification/Android.bp1
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig1
-rw-r--r--services/core/java/com/android/server/os/Android.bp1
-rw-r--r--services/core/java/com/android/server/os/core_os_flags.aconfig1
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java34
-rw-r--r--services/core/java/com/android/server/policy/Android.bp1
-rw-r--r--services/core/java/com/android/server/policy/window_policy_flags.aconfig1
-rw-r--r--services/core/java/com/android/server/power/Android.bp1
-rw-r--r--services/core/java/com/android/server/power/batterysaver/OWNERS3
-rw-r--r--services/core/java/com/android/server/power/feature/Android.bp1
-rw-r--r--services/core/java/com/android/server/power/feature/power_flags.aconfig1
-rw-r--r--services/core/java/com/android/server/power/hint/Android.bp1
-rw-r--r--services/core/java/com/android/server/power/hint/flags.aconfig1
-rw-r--r--services/core/java/com/android/server/power/stats/flags.aconfig1
-rw-r--r--services/core/java/com/android/server/stats/Android.bp1
-rw-r--r--services/core/java/com/android/server/stats/stats_flags.aconfig1
-rw-r--r--services/core/java/com/android/server/utils/Android.bp1
-rw-r--r--services/core/java/com/android/server/utils/flags.aconfig1
-rw-r--r--services/core/java/com/android/server/utils/quota/OWNERS1
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp1
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig1
-rw-r--r--services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/OWNERS2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java126
-rw-r--r--services/tests/selinux/AndroidTest.xml33
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java7
-rw-r--r--tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java116
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java69
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp23
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt54
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt2
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt)12
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt5
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt11
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt113
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt4
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt24
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt11
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt46
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt11
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt56
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java2
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java4
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java11
170 files changed, 1715 insertions, 3591 deletions
diff --git a/apex/jobscheduler/OWNERS b/apex/jobscheduler/OWNERS
index 58434f165aa2..22b648975e5f 100644
--- a/apex/jobscheduler/OWNERS
+++ b/apex/jobscheduler/OWNERS
@@ -2,7 +2,6 @@ ctate@android.com
ctate@google.com
dplotnikov@google.com
jji@google.com
-kwekua@google.com
omakoto@google.com
suprabh@google.com
varunshah@google.com
diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS
index cede4eae50ad..29f44aba75d1 100644
--- a/cmds/incident_helper/OWNERS
+++ b/cmds/incident_helper/OWNERS
@@ -1,3 +1,2 @@
joeo@google.com
-kwekua@google.com
yanmin@google.com
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 75485626d2ef..508077ed43cc 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2655,13 +2655,12 @@ public class AssistStructure implements Parcelable {
);
}
GetCredentialRequest getCredentialRequest = node.getPendingCredentialRequest();
- if (getCredentialRequest == null) {
- Log.i(TAG, prefix + " No Credential Manager Request");
- } else {
- Log.i(TAG, prefix + " GetCredentialRequest: no. of options= "
- + getCredentialRequest.getCredentialOptions().size()
- );
- }
+ Log.i(TAG, prefix + " Credential Manager info:"
+ + " hasCredentialManagerRequest=" + (getCredentialRequest != null)
+ + (getCredentialRequest != null
+ ? ", sizeOfOptions=" + getCredentialRequest.getCredentialOptions().size()
+ : "")
+ );
final int NCHILDREN = node.getChildCount();
if (NCHILDREN > 0) {
diff --git a/core/java/com/android/internal/compat/Android.bp b/core/java/com/android/internal/compat/Android.bp
index 9ff05a6589b7..8253aa89b979 100644
--- a/core/java/com/android/internal/compat/Android.bp
+++ b/core/java/com/android/internal/compat/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "compat_logging_flags",
package: "com.android.internal.compat.flags",
+ container: "system",
srcs: [
"compat_logging_flags.aconfig",
],
diff --git a/core/java/com/android/internal/compat/compat_logging_flags.aconfig b/core/java/com/android/internal/compat/compat_logging_flags.aconfig
index a5c31edde473..4f5162693408 100644
--- a/core/java/com/android/internal/compat/compat_logging_flags.aconfig
+++ b/core/java/com/android/internal/compat/compat_logging_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.internal.compat.flags"
+container: "system"
flag {
name: "skip_old_and_disabled_compat_logging"
@@ -6,4 +7,4 @@ flag {
description: "Feature flag for skipping debug logging for changes that do not target the latest sdk or are disabled"
bug: "323949942"
is_fixed_read_only: true
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/foldables/Android.bp b/core/java/com/android/internal/foldables/Android.bp
index f1d06da98186..53a9be5fdb0e 100644
--- a/core/java/com/android/internal/foldables/Android.bp
+++ b/core/java/com/android/internal/foldables/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "fold_lock_setting_flags",
package: "com.android.internal.foldables.flags",
+ container: "system",
srcs: [
"fold_lock_setting_flags.aconfig",
],
diff --git a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
index d73e62373732..03b6a5bcf467 100644
--- a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
+++ b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.internal.foldables.flags"
+container: "system"
flag {
name: "fold_lock_setting_enabled"
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 7c976b7473f6..b6bf617b40ae 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -36,43 +36,41 @@ static std::string buildFileName(const std::string& locale) {
return SYSTEM_HYPHENATOR_PREFIX + lowerLocale + SYSTEM_HYPHENATOR_SUFFIX;
}
-static std::pair<const uint8_t*, uint32_t> mmapPatternFile(const std::string& locale) {
+static const uint8_t* mmapPatternFile(const std::string& locale) {
const std::string hyFilePath = buildFileName(locale);
const int fd = open(hyFilePath.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
- return std::make_pair(nullptr, 0); // Open failed.
+ return nullptr; // Open failed.
}
struct stat st = {};
if (fstat(fd, &st) == -1) { // Unlikely to happen.
close(fd);
- return std::make_pair(nullptr, 0);
+ return nullptr;
}
void* ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0 /* offset */);
close(fd);
if (ptr == MAP_FAILED) {
- return std::make_pair(nullptr, 0);
+ return nullptr;
}
- return std::make_pair(reinterpret_cast<const uint8_t*>(ptr), st.st_size);
+ return reinterpret_cast<const uint8_t*>(ptr);
}
static void addHyphenatorWithoutPatternFile(const std::string& locale, int minPrefix,
int minSuffix) {
- minikin::addHyphenator(locale,
- minikin::Hyphenator::loadBinary(nullptr, 0, minPrefix, minSuffix,
- locale));
+ minikin::addHyphenator(locale, minikin::Hyphenator::loadBinary(
+ nullptr, minPrefix, minSuffix, locale));
}
static void addHyphenator(const std::string& locale, int minPrefix, int minSuffix) {
- auto [ptr, size] = mmapPatternFile(locale);
+ const uint8_t* ptr = mmapPatternFile(locale);
if (ptr == nullptr) {
ALOGE("Unable to find pattern file or unable to map it for %s", locale.c_str());
return;
}
- minikin::addHyphenator(locale,
- minikin::Hyphenator::loadBinary(ptr, size, minPrefix, minSuffix,
- locale));
+ minikin::addHyphenator(locale, minikin::Hyphenator::loadBinary(
+ ptr, minPrefix, minSuffix, locale));
}
static void addHyphenatorAlias(const std::string& from, const std::string& to) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index b900fa60ff70..b51f72dee260 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -11,7 +11,6 @@ zhouwenjie@google.com
# Frameworks
ogunwale@google.com
jjaggi@google.com
-kwekua@google.com
roosa@google.com
per-file package_item_info.proto = file:/PACKAGE_MANAGER_OWNERS
per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
diff --git a/graphics/java/Android.bp b/graphics/java/Android.bp
index ece453d1a70e..f4abd0ab582c 100644
--- a/graphics/java/Android.bp
+++ b/graphics/java/Android.bp
@@ -11,6 +11,7 @@ package {
aconfig_declarations {
name: "framework_graphics_flags",
package: "com.android.graphics.flags",
+ container: "system",
srcs: ["android/framework_graphics.aconfig"],
}
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index 1e41b4d9ed1b..4ab09eb7e9f8 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -1,4 +1,5 @@
package: "com.android.graphics.flags"
+container: "system"
flag {
name: "exact_compute_bounds"
@@ -14,4 +15,4 @@ flag {
namespace: "core_graphics"
description: "Feature flag for YUV image compress to Ultra HDR."
bug: "308978825"
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index a541c590575f..c2ba064ac7b6 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -45,9 +45,6 @@
<!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
<bool name="config_pipEnableResizeForMenu">true</bool>
- <!-- PiP minimum size, which is a % based off the shorter side of display width and height -->
- <fraction name="config_pipShortestEdgePercent">40%</fraction>
-
<!-- Time (duration in milliseconds) that the shell waits for an app to close the PiP by itself
if a custom action is present before closing it. -->
<integer name="config_pipForceCloseDelay">1000</integer>
@@ -91,11 +88,45 @@
16x16
</string>
+ <!-- Default percentages for the PIP size logic.
+ 1. Determine max widths
+ Subtract width of system UI and default padding from the shortest edge of the device.
+ This is the max width.
+ 2. Calculate Default and Mins
+ Default is config_pipSystemPreferredDefaultSizePercent of max-width/height.
+ Min is config_pipSystemPreferredMinimumSizePercent of it. -->
+ <item name="config_pipSystemPreferredDefaultSizePercent" format="float" type="dimen">0.6</item>
+ <item name="config_pipSystemPreferredMinimumSizePercent" format="float" type="dimen">0.5</item>
+ <!-- Default percentages for the PIP size logic when the Display is close to square.
+ This is used instead when the display is square-ish, like fold-ables when unfolded,
+ to make sure that default PiP does not cover the hinge (halfway of the display).
+ 0. Determine if the display is square-ish
+ If min(displayWidth, displayHeight) / max(displayWidth, displayHeight) is greater than
+ config_pipSquareDisplayThresholdForSystemPreferredSize, we use the percent for
+ square display listed below.
+ 1. Determine max widths
+ Subtract width of system UI and default padding from the shortest edge of the device.
+ This is the max width.
+ 2. Calculate Default and Mins
+ Default is config_pipSystemPreferredDefaultSizePercentForSquareDisplay of max-width/height.
+ Min is config_pipSystemPreferredMinimumSizePercentForSquareDisplay of it. -->
+ <item name="config_pipSquareDisplayThresholdForSystemPreferredSize"
+ format="float" type="dimen">0.95</item>
+ <item name="config_pipSystemPreferredDefaultSizePercentForSquareDisplay"
+ format="float" type="dimen">0.5</item>
+ <item name="config_pipSystemPreferredMinimumSizePercentForSquareDisplay"
+ format="float" type="dimen">0.4</item>
+
<!-- The percentage of the screen width to use for the default width or height of
picture-in-picture windows. Regardless of the percent set here, calculated size will never
- be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
+ be smaller than @dimen/default_minimal_size_pip_resizable_task.
+ This is used in legacy spec, use config_pipSystemPreferredDefaultSizePercent instead. -->
<item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item>
+ <!-- PiP minimum size, which is a % based off the shorter side of display width and height.
+ This is used in legacy spec, use config_pipSystemPreferredMinimumSizePercent instead. -->
+ <fraction name="config_pipShortestEdgePercent">40%</fraction>
+
<!-- The default aspect ratio for picture-in-picture windows. -->
<item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen">
1.777778
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 0867a44c1998..313d0d24b459 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
@@ -2335,6 +2335,11 @@ public class BubbleController implements ConfigurationChangeListener,
mMainExecutor.execute(() ->
mController.setBubbleBarLocation(location));
}
+
+ @Override
+ public void setBubbleBarBounds(Rect bubbleBarBounds) {
+ mMainExecutor.execute(() -> mBubblePositioner.setBubbleBarBounds(bubbleBarBounds));
+ }
}
private class BubblesImpl implements Bubbles {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 16134d3e5a70..c9f0f0d61713 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -44,4 +44,6 @@ interface IBubbles {
oneway void showUserEducation(in int positionX, in int positionY) = 8;
oneway void setBubbleBarLocation(in BubbleBarLocation location) = 9;
+
+ oneway void setBubbleBarBounds(in Rect bubbleBarBounds) = 10;
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 8af4c75b5733..45ad6319bbf8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -166,13 +166,8 @@ public class BubbleBarAnimationHelper {
bbev.setTaskViewAlpha(0f);
bbev.setVisibility(VISIBLE);
- // Set the pivot point for the scale, so the view animates out from the bubble bar.
- Rect bubbleBarBounds = mPositioner.getBubbleBarBounds();
- mExpandedViewContainerMatrix.setScale(
- 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
- 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
- bubbleBarBounds.centerX(),
- bubbleBarBounds.top);
+ setScaleFromBubbleBar(mExpandedViewContainerMatrix,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT);
bbev.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -214,8 +209,8 @@ public class BubbleBarAnimationHelper {
}
bbev.setScaleX(1f);
bbev.setScaleY(1f);
- mExpandedViewContainerMatrix.setScaleX(1f);
- mExpandedViewContainerMatrix.setScaleY(1f);
+
+ setScaleFromBubbleBar(mExpandedViewContainerMatrix, 1f);
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
@@ -240,6 +235,16 @@ public class BubbleBarAnimationHelper {
mExpandedViewAlphaAnimator.reverse();
}
+ private void setScaleFromBubbleBar(AnimatableScaleMatrix matrix, float scale) {
+ // Set the pivot point for the scale, so the view animates out from the bubble bar.
+ Rect bubbleBarBounds = mPositioner.getBubbleBarBounds();
+ matrix.setScale(
+ scale,
+ scale,
+ bubbleBarBounds.centerX(),
+ bubbleBarBounds.top);
+ }
+
/**
* Animate the expanded bubble when it is being dragged
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
index 18c7bdd6d5ba..7eb0f267b312 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
@@ -18,7 +18,6 @@ package com.android.wm.shell.common.pip
import android.content.Context
import android.content.res.Resources
-import android.os.SystemProperties
import android.util.Size
import com.android.wm.shell.R
import java.io.PrintWriter
@@ -36,30 +35,81 @@ class PhoneSizeSpecSource(
private var mOverrideMinSize: Size? = null
- /** Default and minimum percentages for the PIP size logic. */
- private val mDefaultSizePercent: Float
- private val mMinimumSizePercent: Float
+ /**
+ * Default percentages for the PIP size logic.
+ * 1. Determine max widths
+ * Subtract width of system UI and default padding from the shortest edge of the device.
+ * This is the max width.
+ * 2. Calculate Default and Mins
+ * Default is mSystemPreferredDefaultSizePercent of max-width/height.
+ * Min is mSystemPreferredMinimumSizePercent of it.
+ *
+ * NOTE: Do not use this directly, use the mPreferredDefaultSizePercent getter instead.
+ */
+ private var mSystemPreferredDefaultSizePercent = 0.6f
+ /** Minimum percentages for the PIP size logic. */
+ private var mSystemPreferredMinimumSizePercent = 0.5f
+
+ /** Threshold to categorize the Display as square, calculated as min(w, h) / max(w, h). */
+ private var mSquareDisplayThresholdForSystemPreferredSize = 0.95f
+ /**
+ * Default percentages for the PIP size logic when the Display is square-ish.
+ * This is used instead when the display is square-ish, like fold-ables when unfolded,
+ * to make sure that default PiP does not cover the hinge (halfway of the display).
+ * 1. Determine max widths
+ * Subtract width of system UI and default padding from the shortest edge of the device.
+ * This is the max width.
+ * 2. Calculate Default and Mins
+ * Default is mSystemPreferredDefaultSizePercent of max-width/height.
+ * Min is mSystemPreferredMinimumSizePercent of it.
+ *
+ * NOTE: Do not use this directly, use the mPreferredDefaultSizePercent getter instead.
+ */
+ private var mSystemPreferredDefaultSizePercentForSquareDisplay = 0.5f
+ /** Minimum percentages for the PIP size logic. */
+ private var mSystemPreferredMinimumSizePercentForSquareDisplay = 0.4f
+
+ private val mIsSquareDisplay
+ get() = minOf(pipDisplayLayoutState.displayLayout.width(),
+ pipDisplayLayoutState.displayLayout.height()).toFloat() /
+ maxOf(pipDisplayLayoutState.displayLayout.width(),
+ pipDisplayLayoutState.displayLayout.height()) >
+ mSquareDisplayThresholdForSystemPreferredSize
+ private val mPreferredDefaultSizePercent
+ get() = if (mIsSquareDisplay) mSystemPreferredDefaultSizePercentForSquareDisplay else
+ mSystemPreferredDefaultSizePercent
+
+ private val mPreferredMinimumSizePercent
+ get() = if (mIsSquareDisplay) mSystemPreferredMinimumSizePercentForSquareDisplay else
+ mSystemPreferredMinimumSizePercent
/** Aspect ratio that the PIP size spec logic optimizes for. */
private var mOptimizedAspectRatio = 0f
init {
- mDefaultSizePercent = SystemProperties
- .get("com.android.wm.shell.pip.phone.def_percentage", "0.6").toFloat()
- mMinimumSizePercent = SystemProperties
- .get("com.android.wm.shell.pip.phone.min_percentage", "0.5").toFloat()
-
reloadResources()
}
private fun reloadResources() {
- val res: Resources = context.getResources()
+ val res: Resources = context.resources
mDefaultMinSize = res.getDimensionPixelSize(
R.dimen.default_minimal_size_pip_resizable_task)
mOverridableMinSize = res.getDimensionPixelSize(
R.dimen.overridable_minimal_size_pip_resizable_task)
+ mSystemPreferredDefaultSizePercent = res.getFloat(
+ R.dimen.config_pipSystemPreferredDefaultSizePercent)
+ mSystemPreferredMinimumSizePercent = res.getFloat(
+ R.dimen.config_pipSystemPreferredMinimumSizePercent)
+
+ mSquareDisplayThresholdForSystemPreferredSize = res.getFloat(
+ R.dimen.config_pipSquareDisplayThresholdForSystemPreferredSize)
+ mSystemPreferredDefaultSizePercentForSquareDisplay = res.getFloat(
+ R.dimen.config_pipSystemPreferredDefaultSizePercentForSquareDisplay)
+ mSystemPreferredMinimumSizePercentForSquareDisplay = res.getFloat(
+ R.dimen.config_pipSystemPreferredMinimumSizePercentForSquareDisplay)
+
val requestedOptAspRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio)
// make sure the optimized aspect ratio is valid with a default value to fall back to
mOptimizedAspectRatio = if (requestedOptAspRatio > 1) {
@@ -128,7 +178,7 @@ class PhoneSizeSpecSource(
return minSize
}
val maxSize = getMaxSize(aspectRatio)
- val defaultWidth = Math.max(Math.round(maxSize.width * mDefaultSizePercent),
+ val defaultWidth = Math.max(Math.round(maxSize.width * mPreferredDefaultSizePercent),
minSize.width)
val defaultHeight = Math.round(defaultWidth / aspectRatio)
return Size(defaultWidth, defaultHeight)
@@ -146,8 +196,8 @@ class PhoneSizeSpecSource(
return adjustOverrideMinSizeToAspectRatio(aspectRatio)!!
}
val maxSize = getMaxSize(aspectRatio)
- var minWidth = Math.round(maxSize.width * mMinimumSizePercent)
- var minHeight = Math.round(maxSize.height * mMinimumSizePercent)
+ var minWidth = Math.round(maxSize.width * mPreferredMinimumSizePercent)
+ var minHeight = Math.round(maxSize.height * mPreferredMinimumSizePercent)
// make sure the calculated min size is not smaller than the allowed default min size
if (aspectRatio > 1f) {
@@ -244,8 +294,8 @@ class PhoneSizeSpecSource(
pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize)
pw.println(innerPrefix + "mOverridableMinSize=" + mOverridableMinSize)
pw.println(innerPrefix + "mDefaultMinSize=" + mDefaultMinSize)
- pw.println(innerPrefix + "mDefaultSizePercent=" + mDefaultSizePercent)
- pw.println(innerPrefix + "mMinimumSizePercent=" + mMinimumSizePercent)
+ pw.println(innerPrefix + "mDefaultSizePercent=" + mPreferredDefaultSizePercent)
+ pw.println(innerPrefix + "mMinimumSizePercent=" + mPreferredMinimumSizePercent)
pw.println(innerPrefix + "mOptimizedAspectRatio=" + mOptimizedAspectRatio)
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
index 3d5cd6939d1b..85f1da5322ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
@@ -16,33 +16,26 @@
package com.android.wm.shell.pip.phone;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
-import android.os.SystemProperties;
import android.testing.AndroidTestingRunner;
import android.util.Size;
import android.view.DisplayInfo;
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.exceptions.misusing.InvalidUseOfMatchersException;
import java.util.HashMap;
import java.util.Map;
@@ -63,15 +56,24 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase {
private static final float DEFAULT_PERCENT = 0.6f;
/** Minimum sizing percentage */
private static final float MIN_PERCENT = 0.5f;
+ /** Threshold to determine if a Display is square-ish. */
+ private static final float SQUARE_DISPLAY_THRESHOLD = 0.95f;
+ /** Default sizing percentage for square-ish Display. */
+ private static final float SQUARE_DISPLAY_DEFAULT_PERCENT = 0.5f;
+ /** Minimum sizing percentage for square-ish Display. */
+ private static final float SQUARE_DISPLAY_MIN_PERCENT = 0.4f;
/** Aspect ratio that the new PIP size spec logic optimizes for. */
private static final float OPTIMIZED_ASPECT_RATIO = 9f / 16;
- /** A map of aspect ratios to be tested to expected sizes */
- private static Map<Float, Size> sExpectedMaxSizes;
- private static Map<Float, Size> sExpectedDefaultSizes;
- private static Map<Float, Size> sExpectedMinSizes;
- /** A static mockito session object to mock {@link SystemProperties} */
- private static StaticMockitoSession sStaticMockitoSession;
+ /** Maps of aspect ratios to be tested to expected sizes on non-square Display. */
+ private static Map<Float, Size> sNonSquareDisplayExpectedMaxSizes;
+ private static Map<Float, Size> sNonSquareDisplayExpectedDefaultSizes;
+ private static Map<Float, Size> sNonSquareDisplayExpectedMinSizes;
+
+ /** Maps of aspect ratios to be tested to expected sizes on square Display. */
+ private static Map<Float, Size> sSquareDisplayExpectedMaxSizes;
+ private static Map<Float, Size> sSquareDisplayExpectedDefaultSizes;
+ private static Map<Float, Size> sSquareDisplayExpectedMinSizes;
@Mock private Context mContext;
@Mock private Resources mResources;
@@ -80,49 +82,55 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase {
private SizeSpecSource mSizeSpecSource;
/**
- * Sets up static Mockito session for SystemProperties and mocks necessary static methods.
+ * Initializes the map with the aspect ratios to be tested and corresponding expected max sizes.
+ * This is to initialize the expectations on non-square Display only.
*/
- private static void setUpStaticSystemPropertiesSession() {
- sStaticMockitoSession = mockitoSession()
- .mockStatic(SystemProperties.class).startMocking();
- when(SystemProperties.get(anyString(), anyString())).thenAnswer(invocation -> {
- String property = invocation.getArgument(0);
- if (property.equals("com.android.wm.shell.pip.phone.def_percentage")) {
- return Float.toString(DEFAULT_PERCENT);
- } else if (property.equals("com.android.wm.shell.pip.phone.min_percentage")) {
- return Float.toString(MIN_PERCENT);
- }
-
- // throw an exception if illegal arguments are used for these tests
- throw new InvalidUseOfMatchersException(
- String.format("Argument %s does not match", property)
- );
- });
+ private static void initNonSquareDisplayExpectedSizes() {
+ sNonSquareDisplayExpectedMaxSizes = new HashMap<>();
+ sNonSquareDisplayExpectedDefaultSizes = new HashMap<>();
+ sNonSquareDisplayExpectedMinSizes = new HashMap<>();
+
+ sNonSquareDisplayExpectedMaxSizes.put(16f / 9, new Size(1000, 563));
+ sNonSquareDisplayExpectedDefaultSizes.put(16f / 9, new Size(600, 338));
+ sNonSquareDisplayExpectedMinSizes.put(16f / 9, new Size(501, 282));
+
+ sNonSquareDisplayExpectedMaxSizes.put(4f / 3, new Size(893, 670));
+ sNonSquareDisplayExpectedDefaultSizes.put(4f / 3, new Size(536, 402));
+ sNonSquareDisplayExpectedMinSizes.put(4f / 3, new Size(447, 335));
+
+ sNonSquareDisplayExpectedMaxSizes.put(3f / 4, new Size(670, 893));
+ sNonSquareDisplayExpectedDefaultSizes.put(3f / 4, new Size(402, 536));
+ sNonSquareDisplayExpectedMinSizes.put(3f / 4, new Size(335, 447));
+
+ sNonSquareDisplayExpectedMaxSizes.put(9f / 16, new Size(563, 1001));
+ sNonSquareDisplayExpectedDefaultSizes.put(9f / 16, new Size(338, 601));
+ sNonSquareDisplayExpectedMinSizes.put(9f / 16, new Size(282, 501));
}
/**
* Initializes the map with the aspect ratios to be tested and corresponding expected max sizes.
+ * This is to initialize the expectations on square Display only.
*/
- private static void initExpectedSizes() {
- sExpectedMaxSizes = new HashMap<>();
- sExpectedDefaultSizes = new HashMap<>();
- sExpectedMinSizes = new HashMap<>();
-
- sExpectedMaxSizes.put(16f / 9, new Size(1000, 563));
- sExpectedDefaultSizes.put(16f / 9, new Size(600, 338));
- sExpectedMinSizes.put(16f / 9, new Size(501, 282));
-
- sExpectedMaxSizes.put(4f / 3, new Size(893, 670));
- sExpectedDefaultSizes.put(4f / 3, new Size(536, 402));
- sExpectedMinSizes.put(4f / 3, new Size(447, 335));
-
- sExpectedMaxSizes.put(3f / 4, new Size(670, 893));
- sExpectedDefaultSizes.put(3f / 4, new Size(402, 536));
- sExpectedMinSizes.put(3f / 4, new Size(335, 447));
-
- sExpectedMaxSizes.put(9f / 16, new Size(563, 1001));
- sExpectedDefaultSizes.put(9f / 16, new Size(338, 601));
- sExpectedMinSizes.put(9f / 16, new Size(282, 501));
+ private static void initSquareDisplayExpectedSizes() {
+ sSquareDisplayExpectedMaxSizes = new HashMap<>();
+ sSquareDisplayExpectedDefaultSizes = new HashMap<>();
+ sSquareDisplayExpectedMinSizes = new HashMap<>();
+
+ sSquareDisplayExpectedMaxSizes.put(16f / 9, new Size(1000, 563));
+ sSquareDisplayExpectedDefaultSizes.put(16f / 9, new Size(500, 281));
+ sSquareDisplayExpectedMinSizes.put(16f / 9, new Size(400, 225));
+
+ sSquareDisplayExpectedMaxSizes.put(4f / 3, new Size(893, 670));
+ sSquareDisplayExpectedDefaultSizes.put(4f / 3, new Size(447, 335));
+ sSquareDisplayExpectedMinSizes.put(4f / 3, new Size(357, 268));
+
+ sSquareDisplayExpectedMaxSizes.put(3f / 4, new Size(670, 893));
+ sSquareDisplayExpectedDefaultSizes.put(3f / 4, new Size(335, 447));
+ sSquareDisplayExpectedMinSizes.put(3f / 4, new Size(268, 357));
+
+ sSquareDisplayExpectedMaxSizes.put(9f / 16, new Size(563, 1001));
+ sSquareDisplayExpectedDefaultSizes.put(9f / 16, new Size(282, 501));
+ sSquareDisplayExpectedMinSizes.put(9f / 16, new Size(225, 400));
}
private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes,
@@ -137,20 +145,38 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase {
@Before
public void setUp() {
- initExpectedSizes();
-
- when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE);
- when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO);
- when(mResources.getString(anyInt())).thenReturn("0x0");
+ initNonSquareDisplayExpectedSizes();
+ initSquareDisplayExpectedSizes();
+
+ when(mResources.getFloat(R.dimen.config_pipSystemPreferredDefaultSizePercent))
+ .thenReturn(DEFAULT_PERCENT);
+ when(mResources.getFloat(R.dimen.config_pipSystemPreferredMinimumSizePercent))
+ .thenReturn(MIN_PERCENT);
+ when(mResources.getDimensionPixelSize(R.dimen.default_minimal_size_pip_resizable_task))
+ .thenReturn(DEFAULT_MIN_EDGE_SIZE);
+ when(mResources.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio))
+ .thenReturn(OPTIMIZED_ASPECT_RATIO);
+ when(mResources.getString(R.string.config_defaultPictureInPictureScreenEdgeInsets))
+ .thenReturn("0x0");
when(mResources.getDisplayMetrics())
.thenReturn(getContext().getResources().getDisplayMetrics());
+ when(mResources.getFloat(R.dimen.config_pipSquareDisplayThresholdForSystemPreferredSize))
+ .thenReturn(SQUARE_DISPLAY_THRESHOLD);
+ when(mResources.getFloat(
+ R.dimen.config_pipSystemPreferredDefaultSizePercentForSquareDisplay))
+ .thenReturn(SQUARE_DISPLAY_DEFAULT_PERCENT);
+ when(mResources.getFloat(
+ R.dimen.config_pipSystemPreferredMinimumSizePercentForSquareDisplay))
+ .thenReturn(SQUARE_DISPLAY_MIN_PERCENT);
// set up the mock context for spec handler specifically
when(mContext.getResources()).thenReturn(mResources);
+ }
+ private void setupSizeSpecWithDisplayDimension(int width, int height) {
DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = DISPLAY_EDGE_SIZE;
- displayInfo.logicalHeight = DISPLAY_EDGE_SIZE;
+ displayInfo.logicalWidth = width;
+ displayInfo.logicalHeight = height;
// use the parent context (not the mocked one) to obtain the display layout
// this is done to avoid unnecessary mocking while allowing for custom display dimensions
@@ -159,38 +185,57 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase {
mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
mPipDisplayLayoutState.setDisplayLayout(displayLayout);
- setUpStaticSystemPropertiesSession();
mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState);
// no overridden min edge size by default
mSizeSpecSource.setOverrideMinSize(null);
}
- @After
- public void cleanUp() {
- sStaticMockitoSession.finishMocking();
+ @Test
+ public void testGetMaxSize_nonSquareDisplay() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE);
+ forEveryTestCaseCheck(sNonSquareDisplayExpectedMaxSizes,
+ (aspectRatio) -> mSizeSpecSource.getMaxSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetDefaultSize_nonSquareDisplay() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE);
+ forEveryTestCaseCheck(sNonSquareDisplayExpectedDefaultSizes,
+ (aspectRatio) -> mSizeSpecSource.getDefaultSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetMinSize_nonSquareDisplay() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE);
+ forEveryTestCaseCheck(sNonSquareDisplayExpectedMinSizes,
+ (aspectRatio) -> mSizeSpecSource.getMinSize(aspectRatio));
}
@Test
- public void testGetMaxSize() {
- forEveryTestCaseCheck(sExpectedMaxSizes,
+ public void testGetMaxSize_squareDisplay() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE, DISPLAY_EDGE_SIZE);
+ forEveryTestCaseCheck(sSquareDisplayExpectedMaxSizes,
(aspectRatio) -> mSizeSpecSource.getMaxSize(aspectRatio));
}
@Test
- public void testGetDefaultSize() {
- forEveryTestCaseCheck(sExpectedDefaultSizes,
+ public void testGetDefaultSize_squareDisplay() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE, DISPLAY_EDGE_SIZE);
+ forEveryTestCaseCheck(sSquareDisplayExpectedDefaultSizes,
(aspectRatio) -> mSizeSpecSource.getDefaultSize(aspectRatio));
}
@Test
- public void testGetMinSize() {
- forEveryTestCaseCheck(sExpectedMinSizes,
+ public void testGetMinSize_squareDisplay() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE, DISPLAY_EDGE_SIZE);
+ forEveryTestCaseCheck(sSquareDisplayExpectedMinSizes,
(aspectRatio) -> mSizeSpecSource.getMinSize(aspectRatio));
}
@Test
public void testGetSizeForAspectRatio_noOverrideMinSize() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE);
// an initial size with 16:9 aspect ratio
Size initSize = new Size(600, 337);
@@ -202,6 +247,7 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase {
@Test
public void testGetSizeForAspectRatio_withOverrideMinSize() {
+ setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE);
// an initial size with a 1:1 aspect ratio
Size initSize = new Size(OVERRIDE_MIN_EDGE_SIZE, OVERRIDE_MIN_EDGE_SIZE);
mSizeSpecSource.setOverrideMinSize(initSize);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 341c3e8cf373..02fa5f2608ab 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -31,6 +31,7 @@ license {
aconfig_declarations {
name: "hwui_flags",
package: "com.android.graphics.hwui.flags",
+ container: "system",
srcs: [
"aconfig/hwui_flags.aconfig",
],
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 659bcdc6852d..50f8b3929e1e 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.graphics.hwui.flags"
+container: "system"
flag {
name: "clip_shader"
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index eaafa595ae4f..6d84e7066839 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -836,7 +836,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
public Builder(@NonNull FadeManagerConfiguration fmc) {
mFadeState = fmc.mFadeState;
- mUsageToFadeWrapperMap = fmc.mUsageToFadeWrapperMap.clone();
+ copyUsageToFadeWrapperMapInternal(fmc.mUsageToFadeWrapperMap);
mAttrToFadeWrapperMap = new ArrayMap<AudioAttributes, FadeVolumeShaperConfigsWrapper>(
fmc.mAttrToFadeWrapperMap);
mFadeableUsages = fmc.mFadeableUsages.clone();
@@ -1459,6 +1459,14 @@ public final class FadeManagerConfiguration implements Parcelable {
}
}
+ private void copyUsageToFadeWrapperMapInternal(
+ SparseArray<FadeVolumeShaperConfigsWrapper> usageToFadeWrapperMap) {
+ for (int index = 0; index < usageToFadeWrapperMap.size(); index++) {
+ mUsageToFadeWrapperMap.put(usageToFadeWrapperMap.keyAt(index),
+ new FadeVolumeShaperConfigsWrapper(usageToFadeWrapperMap.valueAt(index)));
+ }
+ }
+
private void validateFadeState(int state) {
switch(state) {
case FADE_STATE_DISABLED:
@@ -1551,6 +1559,12 @@ public final class FadeManagerConfiguration implements Parcelable {
FadeVolumeShaperConfigsWrapper() {}
+ FadeVolumeShaperConfigsWrapper(@NonNull FadeVolumeShaperConfigsWrapper wrapper) {
+ Objects.requireNonNull(wrapper, "Fade volume shaper configs wrapper cannot be null");
+ this.mFadeOutVolShaperConfig = wrapper.mFadeOutVolShaperConfig;
+ this.mFadeInVolShaperConfig = wrapper.mFadeInVolShaperConfig;
+ }
+
public void setFadeOutVolShaperConfig(@Nullable VolumeShaper.Configuration fadeOutConfig) {
mFadeOutVolShaperConfig = fadeOutConfig;
}
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 50fdb8aaf3cd..91c4f118658a 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -25,13 +25,6 @@ flag {
}
flag {
- name: "disable_screen_off_broadcast_receiver"
- namespace: "media_solutions"
- description: "Disables the broadcast receiver that prevents scanning when the screen is off."
- bug: "304234628"
-}
-
-flag {
name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
namespace: "media_solutions"
description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
@@ -108,6 +101,16 @@ flag {
}
flag {
+ name: "enable_mr2_service_non_main_bg_thread"
+ namespace: "media_solutions"
+ description: "Enables the use of a background thread in the media routing framework, instead of using the main thread."
+ bug: "310145678"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_screen_off_scanning"
is_exported: true
namespace: "media_solutions"
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
index c48a95625e8f..74b5afe9b65b 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
@@ -196,8 +196,7 @@ public final class FadeManagerConfigurationUnitTest {
FadeManagerConfiguration fmcObj = new FadeManagerConfiguration
.Builder(TEST_FADE_OUT_DURATION_MS, TEST_FADE_IN_DURATION_MS).build();
- FadeManagerConfiguration fmc = new FadeManagerConfiguration
- .Builder(fmcObj).build();
+ FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder(fmcObj).build();
expect.withMessage("Fade state for copy builder").that(fmc.getFadeState())
.isEqualTo(fmcObj.getFadeState());
@@ -249,6 +248,45 @@ public final class FadeManagerConfigurationUnitTest {
}
@Test
+ public void build_withCopyConstructor_doesnotChangeOriginal() {
+ FadeManagerConfiguration copyConstructedFmc = new FadeManagerConfiguration.Builder(mFmc)
+ .setFadeOutDurationForUsage(AudioAttributes.USAGE_MEDIA, TEST_FADE_OUT_DURATION_MS)
+ .setFadeInDurationForUsage(AudioAttributes.USAGE_MEDIA, TEST_FADE_IN_DURATION_MS)
+ .build();
+
+ expect.withMessage("Fade out duration for media usage of default constructor")
+ .that(mFmc.getFadeOutDurationForUsage(AudioAttributes.USAGE_MEDIA))
+ .isEqualTo(DEFAULT_FADE_OUT_DURATION_MS);
+ expect.withMessage("Fade out duration for media usage of default constructor")
+ .that(mFmc.getFadeInDurationForUsage(AudioAttributes.USAGE_MEDIA))
+ .isEqualTo(DEFAULT_FADE_IN_DURATION_MS);
+ expect.withMessage("Fade out duration for media usage of copy constructor")
+ .that(copyConstructedFmc.getFadeOutDurationForUsage(AudioAttributes.USAGE_MEDIA))
+ .isEqualTo(TEST_FADE_OUT_DURATION_MS);
+ expect.withMessage("Fade out duration for media usage of copy constructor")
+ .that(copyConstructedFmc.getFadeInDurationForUsage(AudioAttributes.USAGE_MEDIA))
+ .isEqualTo(TEST_FADE_IN_DURATION_MS);
+ }
+
+ @Test
+ public void build_withCopyConstructor_equals() {
+ FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder()
+ .setFadeableUsages(List.of(AudioAttributes.USAGE_MEDIA,
+ AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ AudioAttributes.USAGE_ASSISTANT,
+ AudioAttributes.USAGE_EMERGENCY))
+ .setFadeOutDurationForUsage(AudioAttributes.USAGE_MEDIA, TEST_FADE_OUT_DURATION_MS)
+ .setFadeInDurationForUsage(AudioAttributes.USAGE_MEDIA, TEST_FADE_IN_DURATION_MS)
+ .build();
+
+ FadeManagerConfiguration copyConstructedFmc =
+ new FadeManagerConfiguration.Builder(fmc).build();
+
+ expect.withMessage("Fade manager config constructed using copy constructor").that(fmc)
+ .isEqualTo(copyConstructedFmc);
+ }
+
+ @Test
public void testGetDefaultFadeOutDuration() {
expect.withMessage("Default fade out duration")
.that(FadeManagerConfiguration.getDefaultFadeOutDurationMillis())
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 9b1330fc048a..6ce83cd7b765 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -367,7 +367,7 @@ void ASurfaceTransaction_reparent(ASurfaceTransaction* aSurfaceTransaction,
void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
- int8_t visibility) {
+ ASurfaceTransactionVisibility visibility) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
@@ -496,7 +496,7 @@ void ASurfaceTransaction_setScale(ASurfaceTransaction* aSurfaceTransaction,
void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
- int8_t transparency) {
+ ASurfaceTransactionTransparency transparency) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
index a8d8f9a1a55d..6f20adf74ee2 100644
--- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
@@ -39,15 +39,15 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.Xml;
-import android.utils.BackgroundThread;
-import android.utils.LongArrayQueue;
-import android.utils.XmlUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -137,17 +137,6 @@ public class PackageWatchdog {
static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5;
static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10);
- // Boot loop at which packageWatchdog starts first mitigation
- private static final String BOOT_LOOP_THRESHOLD =
- "persist.device_config.configuration.boot_loop_threshold";
- @VisibleForTesting
- static final int DEFAULT_BOOT_LOOP_THRESHOLD = 15;
- // Once boot_loop_threshold is surpassed next mitigation would be triggered after
- // specified number of reboots.
- private static final String BOOT_LOOP_MITIGATION_INCREMENT =
- "persist.device_config.configuration..boot_loop_mitigation_increment";
- @VisibleForTesting
- static final int DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT = 2;
// Threshold level at which or above user might experience significant disruption.
private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
@@ -253,15 +242,8 @@ public class PackageWatchdog {
mConnectivityModuleConnector = connectivityModuleConnector;
mSystemClock = clock;
mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
- if (Flags.recoverabilityDetection()) {
- mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
- DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS,
- SystemProperties.getInt(BOOT_LOOP_MITIGATION_INCREMENT,
- DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT));
- } else {
- mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
- DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
- }
+ mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
loadFromFile();
sPackageWatchdog = this;
@@ -526,10 +508,16 @@ public class PackageWatchdog {
/**
* Called when the system server boots. If the system server is detected to be in a boot loop,
* query each observer and perform the mitigation action with the lowest user impact.
+ *
+ * Note: PackageWatchdog considers system_server restart loop as bootloop. Full reboots
+ * are not counted in bootloop.
*/
@SuppressWarnings("GuardedBy")
public void noteBoot() {
synchronized (mLock) {
+ // if boot count has reached threshold, start mitigation.
+ // We wait until threshold number of restarts only for the first time. Perform
+ // mitigations for every restart after that.
boolean mitigate = mBootThreshold.incrementAndTest();
if (mitigate) {
if (!Flags.recoverabilityDetection()) {
@@ -557,17 +545,13 @@ public class PackageWatchdog {
}
if (currentObserverToNotify != null) {
if (Flags.recoverabilityDetection()) {
- if (currentObserverImpact < getUserImpactLevelLimit()
- || (currentObserverImpact >= getUserImpactLevelLimit()
- && mBootThreshold.getCount() >= getBootLoopThreshold())) {
- int currentObserverMitigationCount =
- currentObserverInternal.getBootMitigationCount() + 1;
- currentObserverInternal.setBootMitigationCount(
- currentObserverMitigationCount);
- saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
- currentObserverToNotify.executeBootLoopMitigation(
- currentObserverMitigationCount);
- }
+ int currentObserverMitigationCount =
+ currentObserverInternal.getBootMitigationCount() + 1;
+ currentObserverInternal.setBootMitigationCount(
+ currentObserverMitigationCount);
+ saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
+ currentObserverToNotify.executeBootLoopMitigation(
+ currentObserverMitigationCount);
} else {
mBootThreshold.setMitigationCount(mitigationCount);
mBootThreshold.saveMitigationCountToMetadata();
@@ -647,11 +631,6 @@ public class PackageWatchdog {
DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD);
}
- private int getBootLoopThreshold() {
- return SystemProperties.getInt(BOOT_LOOP_THRESHOLD,
- DEFAULT_BOOT_LOOP_THRESHOLD);
- }
-
/** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
@Retention(SOURCE)
@IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
@@ -1827,16 +1806,10 @@ public class PackageWatchdog {
private final int mBootTriggerCount;
private final long mTriggerWindow;
- private final int mBootMitigationIncrement;
BootThreshold(int bootTriggerCount, long triggerWindow) {
- this(bootTriggerCount, triggerWindow, /*bootMitigationIncrement=*/ 1);
- }
-
- BootThreshold(int bootTriggerCount, long triggerWindow, int bootMitigationIncrement) {
this.mBootTriggerCount = bootTriggerCount;
this.mTriggerWindow = triggerWindow;
- this.mBootMitigationIncrement = bootMitigationIncrement;
}
public void reset() {
@@ -1915,6 +1888,7 @@ public class PackageWatchdog {
} else {
readMitigationCountFromMetadataIfNecessary();
}
+
final long now = mSystemClock.uptimeMillis();
if (now - getStart() < 0) {
Slog.e(TAG, "Window was less than zero. Resetting start to current time.");
@@ -1939,20 +1913,32 @@ public class PackageWatchdog {
setCount(count);
EventLogTags.writeRescueNote(Process.ROOT_UID, count, window);
if (Flags.recoverabilityDetection()) {
- boolean mitigate = (count >= mBootTriggerCount)
- && (count - mBootTriggerCount) % mBootMitigationIncrement == 0;
- return mitigate;
+ // After a reboot (e.g. by WARM_REBOOT or mainline rollback) we apply
+ // mitigations without waiting for DEFAULT_BOOT_LOOP_TRIGGER_COUNT.
+ return (count >= mBootTriggerCount)
+ || (performedMitigationsDuringWindow() && count > 1);
}
return count >= mBootTriggerCount;
}
}
@GuardedBy("mLock")
+ private boolean performedMitigationsDuringWindow() {
+ for (ObserverInternal observerInternal: mAllObservers.values()) {
+ if (observerInternal.getBootMitigationCount() > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @GuardedBy("mLock")
private void resetAllObserversBootMitigationCount() {
for (int i = 0; i < mAllObservers.size(); i++) {
final ObserverInternal observer = mAllObservers.valueAt(i);
observer.setBootMitigationCount(0);
}
+ saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
}
@GuardedBy("mLock")
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index f86eb61c365f..271d552fc574 100644
--- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
@@ -31,6 +31,7 @@ import android.content.pm.VersionedPackage;
import android.crashrecovery.flags.Flags;
import android.os.Build;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.SystemClock;
@@ -43,11 +44,10 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
-import android.utils.ArrayUtils;
-import android.utils.FileUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
diff --git a/packages/CrashRecovery/services/java/com/android/utils/ArrayUtils.java b/packages/CrashRecovery/services/java/com/android/utils/ArrayUtils.java
deleted file mode 100644
index fa4d6afc03d3..000000000000
--- a/packages/CrashRecovery/services/java/com/android/utils/ArrayUtils.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.utils;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.io.File;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java
- *
- * @hide
- */
-public class ArrayUtils {
- private ArrayUtils() { /* cannot be instantiated */ }
- public static final File[] EMPTY_FILE = new File[0];
-
-
- /**
- * Return first index of {@code value} in {@code array}, or {@code -1} if
- * not found.
- */
- public static <T> int indexOf(@Nullable T[] array, T value) {
- if (array == null) return -1;
- for (int i = 0; i < array.length; i++) {
- if (Objects.equals(array[i], value)) return i;
- }
- return -1;
- }
-
- /** @hide */
- public static @NonNull File[] defeatNullable(@Nullable File[] val) {
- return (val != null) ? val : EMPTY_FILE;
- }
-
- /**
- * Checks if given array is null or has zero elements.
- */
- public static boolean isEmpty(@Nullable int[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * True if the byte array is null or has length 0.
- */
- public static boolean isEmpty(@Nullable byte[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * Converts from List of bytes to byte array
- * @param list
- * @return byte[]
- */
- public static byte[] toPrimitive(List<byte[]> list) {
- if (list.size() == 0) {
- return new byte[0];
- }
- int byteLen = list.get(0).length;
- byte[] array = new byte[list.size() * byteLen];
- for (int i = 0; i < list.size(); i++) {
- for (int j = 0; j < list.get(i).length; j++) {
- array[i * byteLen + j] = list.get(i)[j];
- }
- }
- return array;
- }
-
- /**
- * Adds value to given array if not already present, providing set-like
- * behavior.
- */
- public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
- return appendInt(cur, val, false);
- }
-
- /**
- * Adds value to given array.
- */
- public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
- boolean allowDuplicates) {
- if (cur == null) {
- return new int[] { val };
- }
- final int n = cur.length;
- if (!allowDuplicates) {
- for (int i = 0; i < n; i++) {
- if (cur[i] == val) {
- return cur;
- }
- }
- }
- int[] ret = new int[n + 1];
- System.arraycopy(cur, 0, ret, 0, n);
- ret[n] = val;
- return ret;
- }
-}
diff --git a/packages/CrashRecovery/services/java/com/android/utils/BackgroundThread.java b/packages/CrashRecovery/services/java/com/android/utils/BackgroundThread.java
deleted file mode 100644
index afcf6895fd0d..000000000000
--- a/packages/CrashRecovery/services/java/com/android/utils/BackgroundThread.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * * 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.utils;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.HandlerThread;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.concurrent.Executor;
-
-/**
- * Thread for asynchronous event processing. This thread is configured as
- * {@link android.os.Process#THREAD_PRIORITY_BACKGROUND}, which means fewer CPU
- * resources will be dedicated to it, and it will "have less chance of impacting
- * the responsiveness of the user interface."
- * <p>
- * This thread is best suited for tasks that the user is not actively waiting
- * for, or for tasks that the user expects to be executed eventually.
- *
- * @see com.android.internal.os.BackgroundThread
- *
- * TODO: b/326916057 depend on modules-utils-backgroundthread instead
- * @hide
- */
-public final class BackgroundThread extends HandlerThread {
- private static final Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static BackgroundThread sInstance;
- @GuardedBy("sLock")
- private static Handler sHandler;
- @GuardedBy("sLock")
- private static HandlerExecutor sHandlerExecutor;
-
- private BackgroundThread() {
- super(BackgroundThread.class.getName(), android.os.Process.THREAD_PRIORITY_BACKGROUND);
- }
-
- @GuardedBy("sLock")
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new BackgroundThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- sHandlerExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- /**
- * Get the singleton instance of this class.
- *
- * @return the singleton instance of this class
- */
- @NonNull
- public static BackgroundThread get() {
- synchronized (sLock) {
- ensureThreadLocked();
- return sInstance;
- }
- }
-
- /**
- * Get the singleton {@link Handler} for this class.
- *
- * @return the singleton {@link Handler} for this class.
- */
- @NonNull
- public static Handler getHandler() {
- synchronized (sLock) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-
- /**
- * Get the singleton {@link Executor} for this class.
- *
- * @return the singleton {@link Executor} for this class.
- */
- @NonNull
- public static Executor getExecutor() {
- synchronized (sLock) {
- ensureThreadLocked();
- return sHandlerExecutor;
- }
- }
-}
diff --git a/packages/CrashRecovery/services/java/com/android/utils/FileUtils.java b/packages/CrashRecovery/services/java/com/android/utils/FileUtils.java
deleted file mode 100644
index e4923bfc4ecb..000000000000
--- a/packages/CrashRecovery/services/java/com/android/utils/FileUtils.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.utils;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Bits and pieces copied from hidden API of android.os.FileUtils.
- *
- * @hide
- */
-public class FileUtils {
- /**
- * Read a text file into a String, optionally limiting the length.
- *
- * @param file to read (will not seek, so things like /proc files are OK)
- * @param max length (positive for head, negative of tail, 0 for no limit)
- * @param ellipsis to add of the file was truncated (can be null)
- * @return the contents of the file, possibly truncated
- * @throws IOException if something goes wrong reading the file
- * @hide
- */
- public static @Nullable String readTextFile(@Nullable File file, @Nullable int max,
- @Nullable String ellipsis) throws IOException {
- InputStream input = new FileInputStream(file);
- // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
- // input stream, bytes read not equal to buffer size is not necessarily the correct
- // indication for EOF; but it is true for BufferedInputStream due to its implementation.
- BufferedInputStream bis = new BufferedInputStream(input);
- try {
- long size = file.length();
- if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes
- if (size > 0 && (max == 0 || size < max)) max = (int) size;
- byte[] data = new byte[max + 1];
- int length = bis.read(data);
- if (length <= 0) return "";
- if (length <= max) return new String(data, 0, length);
- if (ellipsis == null) return new String(data, 0, max);
- return new String(data, 0, max) + ellipsis;
- } else if (max < 0) { // "tail" mode: keep the last N
- int len;
- boolean rolled = false;
- byte[] last = null;
- byte[] data = null;
- do {
- if (last != null) rolled = true;
- byte[] tmp = last;
- last = data;
- data = tmp;
- if (data == null) data = new byte[-max];
- len = bis.read(data);
- } while (len == data.length);
-
- if (last == null && len <= 0) return "";
- if (last == null) return new String(data, 0, len);
- if (len > 0) {
- rolled = true;
- System.arraycopy(last, len, last, 0, last.length - len);
- System.arraycopy(data, 0, last, last.length - len, len);
- }
- if (ellipsis == null || !rolled) return new String(last);
- return ellipsis + new String(last);
- } else { // "cat" mode: size unknown, read it all in streaming fashion
- ByteArrayOutputStream contents = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- do {
- len = bis.read(data);
- if (len > 0) contents.write(data, 0, len);
- } while (len == data.length);
- return contents.toString();
- }
- } finally {
- bis.close();
- input.close();
- }
- }
-
- /**
- * Perform an fsync on the given FileOutputStream. The stream at this
- * point must be flushed but not yet closed.
- *
- * @hide
- */
- public static boolean sync(FileOutputStream stream) {
- try {
- if (stream != null) {
- stream.getFD().sync();
- }
- return true;
- } catch (IOException e) {
- }
- return false;
- }
-
- /**
- * List the files in the directory or return empty file.
- *
- * @hide
- */
- public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
- return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
- : ArrayUtils.EMPTY_FILE;
- }
-}
diff --git a/packages/CrashRecovery/services/java/com/android/utils/HandlerExecutor.java b/packages/CrashRecovery/services/java/com/android/utils/HandlerExecutor.java
deleted file mode 100644
index fdb15e2333d5..000000000000
--- a/packages/CrashRecovery/services/java/com/android/utils/HandlerExecutor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.utils;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * An adapter {@link Executor} that posts all executed tasks onto the given
- * {@link Handler}.
- *
- * TODO: b/326916057 depend on modules-utils-backgroundthread instead
- * @hide
- */
-public class HandlerExecutor implements Executor {
- private final Handler mHandler;
-
- public HandlerExecutor(@NonNull Handler handler) {
- mHandler = Objects.requireNonNull(handler);
- }
-
- @Override
- public void execute(Runnable command) {
- if (!mHandler.post(command)) {
- throw new RejectedExecutionException(mHandler + " is shutting down");
- }
- }
-}
diff --git a/packages/CrashRecovery/services/java/com/android/utils/LongArrayQueue.java b/packages/CrashRecovery/services/java/com/android/utils/LongArrayQueue.java
deleted file mode 100644
index 5cdc2536129a..000000000000
--- a/packages/CrashRecovery/services/java/com/android/utils/LongArrayQueue.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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.utils;
-
-import libcore.util.EmptyArray;
-
-import java.util.NoSuchElementException;
-
-/**
- * Copied from frameworks/base/core/java/android/util/LongArrayQueue.java
- *
- * @hide
- */
-public class LongArrayQueue {
-
- private long[] mValues;
- private int mSize;
- private int mHead;
- private int mTail;
-
- private long[] newUnpaddedLongArray(int num) {
- return new long[num];
- }
- /**
- * Initializes a queue with the given starting capacity.
- *
- * @param initialCapacity the capacity.
- */
- public LongArrayQueue(int initialCapacity) {
- if (initialCapacity == 0) {
- mValues = EmptyArray.LONG;
- } else {
- mValues = newUnpaddedLongArray(initialCapacity);
- }
- mSize = 0;
- mHead = mTail = 0;
- }
-
- /**
- * Initializes a queue with default starting capacity.
- */
- public LongArrayQueue() {
- this(16);
- }
-
- /** @hide */
- public static int growSize(int currentSize) {
- return currentSize <= 4 ? 8 : currentSize * 2;
- }
-
- private void grow() {
- if (mSize < mValues.length) {
- throw new IllegalStateException("Queue not full yet!");
- }
- final int newSize = growSize(mSize);
- final long[] newArray = newUnpaddedLongArray(newSize);
- final int r = mValues.length - mHead; // Number of elements on and to the right of head.
- System.arraycopy(mValues, mHead, newArray, 0, r);
- System.arraycopy(mValues, 0, newArray, r, mHead);
- mValues = newArray;
- mHead = 0;
- mTail = mSize;
- }
-
- /**
- * Returns the number of elements in the queue.
- */
- public int size() {
- return mSize;
- }
-
- /**
- * Removes all elements from this queue.
- */
- public void clear() {
- mSize = 0;
- mHead = mTail = 0;
- }
-
- /**
- * Adds a value to the tail of the queue.
- *
- * @param value the value to be added.
- */
- public void addLast(long value) {
- if (mSize == mValues.length) {
- grow();
- }
- mValues[mTail] = value;
- mTail = (mTail + 1) % mValues.length;
- mSize++;
- }
-
- /**
- * Removes an element from the head of the queue.
- *
- * @return the element at the head of the queue.
- * @throws NoSuchElementException if the queue is empty.
- */
- public long removeFirst() {
- if (mSize == 0) {
- throw new NoSuchElementException("Queue is empty!");
- }
- final long ret = mValues[mHead];
- mHead = (mHead + 1) % mValues.length;
- mSize--;
- return ret;
- }
-
- /**
- * Returns the element at the given position from the head of the queue, where 0 represents the
- * head of the queue.
- *
- * @param position the position from the head of the queue.
- * @return the element found at the given position.
- * @throws IndexOutOfBoundsException if {@code position} < {@code 0} or
- * {@code position} >= {@link #size()}
- */
- public long get(int position) {
- if (position < 0 || position >= mSize) {
- throw new IndexOutOfBoundsException("Index " + position
- + " not valid for a queue of size " + mSize);
- }
- final int index = (mHead + position) % mValues.length;
- return mValues[index];
- }
-
- /**
- * Returns the element at the head of the queue, without removing it.
- *
- * @return the element at the head of the queue.
- * @throws NoSuchElementException if the queue is empty
- */
- public long peekFirst() {
- if (mSize == 0) {
- throw new NoSuchElementException("Queue is empty!");
- }
- return mValues[mHead];
- }
-
- /**
- * Returns the element at the tail of the queue.
- *
- * @return the element at the tail of the queue.
- * @throws NoSuchElementException if the queue is empty.
- */
- public long peekLast() {
- if (mSize == 0) {
- throw new NoSuchElementException("Queue is empty!");
- }
- final int index = (mTail == 0) ? mValues.length - 1 : mTail - 1;
- return mValues[index];
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- if (mSize <= 0) {
- return "{}";
- }
-
- final StringBuilder buffer = new StringBuilder(mSize * 64);
- buffer.append('{');
- buffer.append(get(0));
- for (int i = 1; i < mSize; i++) {
- buffer.append(", ");
- buffer.append(get(i));
- }
- buffer.append('}');
- return buffer.toString();
- }
-}
diff --git a/packages/CrashRecovery/services/java/com/android/utils/XmlUtils.java b/packages/CrashRecovery/services/java/com/android/utils/XmlUtils.java
deleted file mode 100644
index dbbef61f6777..000000000000
--- a/packages/CrashRecovery/services/java/com/android/utils/XmlUtils.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.utils;
-
-import android.annotation.NonNull;
-import android.system.ErrnoException;
-import android.system.Os;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-import libcore.util.XmlObjectFactory;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Copied over partly from frameworks/base/core/java/com/android/internal/util/XmlUtils.java
- *
- * @hide
- */
-public class XmlUtils {
-
- private static final String STRING_ARRAY_SEPARATOR = ":";
-
- /** @hide */
- public static final void beginDocument(XmlPullParser parser, String firstElementName)
- throws XmlPullParserException, IOException {
- int type;
- while ((type = parser.next()) != parser.START_TAG
- && type != parser.END_DOCUMENT) {
- // Do nothing
- }
-
- if (type != parser.START_TAG) {
- throw new XmlPullParserException("No start tag found");
- }
-
- if (!parser.getName().equals(firstElementName)) {
- throw new XmlPullParserException("Unexpected start tag: found " + parser.getName()
- + ", expected " + firstElementName);
- }
- }
-
- /** @hide */
- public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
- throws IOException, XmlPullParserException {
- for (;;) {
- int type = parser.next();
- if (type == XmlPullParser.END_DOCUMENT
- || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
- return false;
- }
- if (type == XmlPullParser.START_TAG
- && parser.getDepth() == outerDepth + 1) {
- return true;
- }
- }
- }
-
- private static XmlPullParser newPullParser() {
- try {
- XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- return parser;
- } catch (XmlPullParserException e) {
- throw new AssertionError();
- }
- }
-
- /** @hide */
- public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
- throws IOException {
- final byte[] magic = new byte[4];
- if (in instanceof FileInputStream) {
- try {
- Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0);
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
- } else {
- if (!in.markSupported()) {
- in = new BufferedInputStream(in);
- }
- in.mark(8);
- in.read(magic);
- in.reset();
- }
-
- final TypedXmlPullParser xml;
- xml = (TypedXmlPullParser) newPullParser();
- try {
- xml.setInput(in, "UTF_8");
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- return xml;
- }
-}
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 79c810ca2611..bd84b58aa0f4 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -46,7 +46,6 @@ android_app {
sdk_version: "system_current",
rename_resources_package: false,
static_libs: [
- "xz-java",
"androidx.leanback_leanback",
"androidx.annotation_annotation",
"androidx.fragment_fragment",
@@ -79,7 +78,6 @@ android_app {
overrides: ["PackageInstaller"],
static_libs: [
- "xz-java",
"androidx.leanback_leanback",
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
@@ -112,7 +110,6 @@ android_app {
overrides: ["PackageInstaller"],
static_libs: [
- "xz-java",
"androidx.leanback_leanback",
"androidx.annotation_annotation",
"androidx.fragment_fragment",
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index bf69d3ba7603..05f4d6954a00 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -146,17 +146,6 @@
android:configChanges="mnc|mnc|touchscreen|navigation|screenLayout|screenSize|smallestScreenSize|orientation|locale|keyboard|keyboardHidden|fontScale|uiMode|layoutDirection|density"
android:exported="false" />
- <!-- Wearable Components -->
- <service android:name=".wear.WearPackageInstallerService"
- android:permission="com.google.android.permission.INSTALL_WEARABLE_PACKAGES"
- android:foregroundServiceType="systemExempted"
- android:exported="true"/>
-
- <provider android:name=".wear.WearPackageIconProvider"
- android:authorities="com.google.android.packageinstaller.wear.provider"
- android:grantUriPermissions="true"
- android:exported="false" />
-
<receiver android:name="androidx.profileinstaller.ProfileInstallReceiver"
tools:node="remove" />
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java
deleted file mode 100644
index 53a460dc18ca..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2016 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.packageinstaller.wear;
-
-import android.content.Context;
-import android.content.IntentSender;
-import android.content.pm.PackageInstaller;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Task that installs an APK. This must not be called on the main thread.
- * This code is based off the Finsky/Wearsky implementation
- */
-public class InstallTask {
- private static final String TAG = "InstallTask";
-
- private static final int DEFAULT_BUFFER_SIZE = 8192;
-
- private final Context mContext;
- private String mPackageName;
- private ParcelFileDescriptor mParcelFileDescriptor;
- private PackageInstallerImpl.InstallListener mCallback;
- private PackageInstaller.Session mSession;
- private IntentSender mCommitCallback;
-
- private Exception mException = null;
- private int mErrorCode = 0;
- private String mErrorDesc = null;
-
- public InstallTask(Context context, String packageName,
- ParcelFileDescriptor parcelFileDescriptor,
- PackageInstallerImpl.InstallListener callback, PackageInstaller.Session session,
- IntentSender commitCallback) {
- mContext = context;
- mPackageName = packageName;
- mParcelFileDescriptor = parcelFileDescriptor;
- mCallback = callback;
- mSession = session;
- mCommitCallback = commitCallback;
- }
-
- public boolean isError() {
- return mErrorCode != InstallerConstants.STATUS_SUCCESS || !TextUtils.isEmpty(mErrorDesc);
- }
-
- public void execute() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new IllegalStateException("This method cannot be called from the UI thread.");
- }
-
- OutputStream sessionStream = null;
- try {
- sessionStream = mSession.openWrite(mPackageName, 0, -1);
-
- // 2b: Stream the asset to the installer. Note:
- // Note: writeToOutputStreamFromAsset() always safely closes the input stream
- writeToOutputStreamFromAsset(sessionStream);
- mSession.fsync(sessionStream);
- } catch (Exception e) {
- mException = e;
- mErrorCode = InstallerConstants.ERROR_INSTALL_COPY_STREAM;
- mErrorDesc = "Could not write to stream";
- } finally {
- if (sessionStream != null) {
- // 2c: close output stream
- try {
- sessionStream.close();
- } catch (Exception e) {
- // Ignore otherwise
- if (mException == null) {
- mException = e;
- mErrorCode = InstallerConstants.ERROR_INSTALL_CLOSE_STREAM;
- mErrorDesc = "Could not close session stream";
- }
- }
- }
- }
-
- if (mErrorCode != InstallerConstants.STATUS_SUCCESS) {
- // An error occurred, we're done
- Log.e(TAG, "Exception while installing " + mPackageName + ": " + mErrorCode + ", "
- + mErrorDesc + ", " + mException);
- mSession.close();
- mCallback.installFailed(mErrorCode, "[" + mPackageName + "]" + mErrorDesc);
- } else {
- // 3. Commit the session (this actually installs it.) Session map
- // will be cleaned up in the callback.
- mCallback.installBeginning();
- mSession.commit(mCommitCallback);
- mSession.close();
- }
- }
-
- /**
- * {@code PackageInstaller} works with streams. Get the {@code FileDescriptor}
- * corresponding to the {@code Asset} and then write the contents into an
- * {@code OutputStream} that is passed in.
- * <br>
- * The {@code FileDescriptor} is closed but the {@code OutputStream} is not closed.
- */
- private boolean writeToOutputStreamFromAsset(OutputStream outputStream) {
- if (outputStream == null) {
- mErrorCode = InstallerConstants.ERROR_INSTALL_COPY_STREAM_EXCEPTION;
- mErrorDesc = "Got a null OutputStream.";
- return false;
- }
-
- if (mParcelFileDescriptor == null || mParcelFileDescriptor.getFileDescriptor() == null) {
- mErrorCode = InstallerConstants.ERROR_COULD_NOT_GET_FD;
- mErrorDesc = "Could not get FD";
- return false;
- }
-
- InputStream inputStream = null;
- try {
- byte[] inputBuf = new byte[DEFAULT_BUFFER_SIZE];
- int bytesRead;
- inputStream = new ParcelFileDescriptor.AutoCloseInputStream(mParcelFileDescriptor);
-
- while ((bytesRead = inputStream.read(inputBuf)) > -1) {
- if (bytesRead > 0) {
- outputStream.write(inputBuf, 0, bytesRead);
- }
- }
-
- outputStream.flush();
- } catch (IOException e) {
- mErrorCode = InstallerConstants.ERROR_INSTALL_APK_COPY_FAILURE;
- mErrorDesc = "Reading from Asset FD or writing to temp file failed: " + e;
- return false;
- } finally {
- safeClose(inputStream);
- }
-
- return true;
- }
-
- /**
- * Quietly close a closeable resource (e.g. a stream or file). The input may already
- * be closed and it may even be null.
- */
- public static void safeClose(Closeable resource) {
- if (resource != null) {
- try {
- resource.close();
- } catch (IOException ioe) {
- // Catch and discard the error
- }
- }
- }
-} \ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java
deleted file mode 100644
index 3daf3d831d97..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 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.packageinstaller.wear;
-
-/**
- * Constants for Installation / Uninstallation requests.
- * Using the same values as Finsky/Wearsky code for consistency in user analytics of failures
- */
-public class InstallerConstants {
- /** Request succeeded */
- public static final int STATUS_SUCCESS = 0;
-
- /**
- * The new PackageInstaller also returns a small set of less granular error codes, which
- * we'll remap to the range -500 and below to keep away from existing installer codes
- * (which run from -1 to -110).
- */
- public final static int ERROR_PACKAGEINSTALLER_BASE = -500;
-
- public static final int ERROR_COULD_NOT_GET_FD = -603;
- /** This node is not targeted by this request. */
-
- /** The install did not complete because could not create PackageInstaller session */
- public final static int ERROR_INSTALL_CREATE_SESSION = -612;
- /** The install did not complete because could not open PackageInstaller session */
- public final static int ERROR_INSTALL_OPEN_SESSION = -613;
- /** The install did not complete because could not open PackageInstaller output stream */
- public final static int ERROR_INSTALL_OPEN_STREAM = -614;
- /** The install did not complete because of an exception while streaming bytes */
- public final static int ERROR_INSTALL_COPY_STREAM_EXCEPTION = -615;
- /** The install did not complete because of an unexpected exception from PackageInstaller */
- public final static int ERROR_INSTALL_SESSION_EXCEPTION = -616;
- /** The install did not complete because of an unexpected userActionRequired callback */
- public final static int ERROR_INSTALL_USER_ACTION_REQUIRED = -617;
- /** The install did not complete because of an unexpected broadcast (missing fields) */
- public final static int ERROR_INSTALL_MALFORMED_BROADCAST = -618;
- /** The install did not complete because of an error while copying from downloaded file */
- public final static int ERROR_INSTALL_APK_COPY_FAILURE = -619;
- /** The install did not complete because of an error while copying to the PackageInstaller
- * output stream */
- public final static int ERROR_INSTALL_COPY_STREAM = -620;
- /** The install did not complete because of an error while closing the PackageInstaller
- * output stream */
- public final static int ERROR_INSTALL_CLOSE_STREAM = -621;
-} \ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java
deleted file mode 100644
index bdc22cf0e276..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 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.packageinstaller.wear;
-
-import android.content.Context;
-
-/**
- * Factory that creates a Package Installer.
- */
-public class PackageInstallerFactory {
- private static PackageInstallerImpl sPackageInstaller;
-
- /**
- * Return the PackageInstaller shared object. {@code init} should have already been called.
- */
- public synchronized static PackageInstallerImpl getPackageInstaller(Context context) {
- if (sPackageInstaller == null) {
- sPackageInstaller = new PackageInstallerImpl(context);
- }
- return sPackageInstaller;
- }
-} \ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
deleted file mode 100644
index 1e37f15f714d..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2016 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.packageinstaller.wear;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.PackageInstaller;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Implementation of package manager installation using modern PackageInstaller api.
- *
- * Heavily copied from Wearsky/Finsky implementation
- */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class PackageInstallerImpl {
- private static final String TAG = "PackageInstallerImpl";
-
- /** Intent actions used for broadcasts from PackageInstaller back to the local receiver */
- private static final String ACTION_INSTALL_COMMIT =
- "com.android.vending.INTENT_PACKAGE_INSTALL_COMMIT";
-
- private final Context mContext;
- private final PackageInstaller mPackageInstaller;
- private final Map<String, PackageInstaller.SessionInfo> mSessionInfoMap;
- private final Map<String, PackageInstaller.Session> mOpenSessionMap;
-
- public PackageInstallerImpl(Context context) {
- mContext = context.getApplicationContext();
- mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
-
- // Capture a map of known sessions
- // This list will be pruned a bit later (stale sessions will be canceled)
- mSessionInfoMap = new HashMap<String, PackageInstaller.SessionInfo>();
- List<PackageInstaller.SessionInfo> mySessions = mPackageInstaller.getMySessions();
- for (int i = 0; i < mySessions.size(); i++) {
- PackageInstaller.SessionInfo sessionInfo = mySessions.get(i);
- String packageName = sessionInfo.getAppPackageName();
- PackageInstaller.SessionInfo oldInfo = mSessionInfoMap.put(packageName, sessionInfo);
-
- // Checking for old info is strictly for logging purposes
- if (oldInfo != null) {
- Log.w(TAG, "Multiple sessions for " + packageName + " found. Removing " + oldInfo
- .getSessionId() + " & keeping " + mySessions.get(i).getSessionId());
- }
- }
- mOpenSessionMap = new HashMap<String, PackageInstaller.Session>();
- }
-
- /**
- * This callback will be made after an installation attempt succeeds or fails.
- */
- public interface InstallListener {
- /**
- * This callback signals that preflight checks have succeeded and installation
- * is beginning.
- */
- void installBeginning();
-
- /**
- * This callback signals that installation has completed.
- */
- void installSucceeded();
-
- /**
- * This callback signals that installation has failed.
- */
- void installFailed(int errorCode, String errorDesc);
- }
-
- /**
- * This is a placeholder implementation that bundles an entire "session" into a single
- * call. This will be replaced by more granular versions that allow longer session lifetimes,
- * download progress tracking, etc.
- *
- * This must not be called on main thread.
- */
- public void install(final String packageName, ParcelFileDescriptor parcelFileDescriptor,
- final InstallListener callback) {
- // 0. Generic try/catch block because I am not really sure what exceptions (other than
- // IOException) might be thrown by PackageInstaller and I want to handle them
- // at least slightly gracefully.
- try {
- // 1. Create or recover a session, and open it
- // Try recovery first
- PackageInstaller.Session session = null;
- PackageInstaller.SessionInfo sessionInfo = mSessionInfoMap.get(packageName);
- if (sessionInfo != null) {
- // See if it's openable, or already held open
- session = getSession(packageName);
- }
- // If open failed, or there was no session, create a new one and open it.
- // If we cannot create or open here, the failure is terminal.
- if (session == null) {
- try {
- innerCreateSession(packageName);
- } catch (IOException ioe) {
- Log.e(TAG, "Can't create session for " + packageName + ": " + ioe.getMessage());
- callback.installFailed(InstallerConstants.ERROR_INSTALL_CREATE_SESSION,
- "Could not create session");
- mSessionInfoMap.remove(packageName);
- return;
- }
- sessionInfo = mSessionInfoMap.get(packageName);
- try {
- session = mPackageInstaller.openSession(sessionInfo.getSessionId());
- mOpenSessionMap.put(packageName, session);
- } catch (SecurityException se) {
- Log.e(TAG, "Can't open session for " + packageName + ": " + se.getMessage());
- callback.installFailed(InstallerConstants.ERROR_INSTALL_OPEN_SESSION,
- "Can't open session");
- mSessionInfoMap.remove(packageName);
- return;
- }
- }
-
- // 2. Launch task to handle file operations.
- InstallTask task = new InstallTask( mContext, packageName, parcelFileDescriptor,
- callback, session,
- getCommitCallback(packageName, sessionInfo.getSessionId(), callback));
- task.execute();
- if (task.isError()) {
- cancelSession(sessionInfo.getSessionId(), packageName);
- }
- } catch (Exception e) {
- Log.e(TAG, "Unexpected exception while installing: " + packageName + ": "
- + e.getMessage());
- callback.installFailed(InstallerConstants.ERROR_INSTALL_SESSION_EXCEPTION,
- "Unexpected exception while installing " + packageName);
- }
- }
-
- /**
- * Retrieve an existing session. Will open if needed, but does not attempt to create.
- */
- private PackageInstaller.Session getSession(String packageName) {
- // Check for already-open session
- PackageInstaller.Session session = mOpenSessionMap.get(packageName);
- if (session != null) {
- try {
- // Probe the session to ensure that it's still open. This may or may not
- // throw (if non-open), but it may serve as a canary for stale sessions.
- session.getNames();
- return session;
- } catch (IOException ioe) {
- Log.e(TAG, "Stale open session for " + packageName + ": " + ioe.getMessage());
- mOpenSessionMap.remove(packageName);
- } catch (SecurityException se) {
- Log.e(TAG, "Stale open session for " + packageName + ": " + se.getMessage());
- mOpenSessionMap.remove(packageName);
- }
- }
- // Check to see if this is a known session
- PackageInstaller.SessionInfo sessionInfo = mSessionInfoMap.get(packageName);
- if (sessionInfo == null) {
- return null;
- }
- // Try to open it. If we fail here, assume that the SessionInfo was stale.
- try {
- session = mPackageInstaller.openSession(sessionInfo.getSessionId());
- } catch (SecurityException se) {
- Log.w(TAG, "SessionInfo was stale for " + packageName + " - deleting info");
- mSessionInfoMap.remove(packageName);
- return null;
- } catch (IOException ioe) {
- Log.w(TAG, "IOException opening old session for " + ioe.getMessage()
- + " - deleting info");
- mSessionInfoMap.remove(packageName);
- return null;
- }
- mOpenSessionMap.put(packageName, session);
- return session;
- }
-
- /** This version throws an IOException when the session cannot be created */
- private void innerCreateSession(String packageName) throws IOException {
- if (mSessionInfoMap.containsKey(packageName)) {
- Log.w(TAG, "Creating session for " + packageName + " when one already exists");
- return;
- }
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- params.setAppPackageName(packageName);
-
- // IOException may be thrown at this point
- int sessionId = mPackageInstaller.createSession(params);
- PackageInstaller.SessionInfo sessionInfo = mPackageInstaller.getSessionInfo(sessionId);
- mSessionInfoMap.put(packageName, sessionInfo);
- }
-
- /**
- * Cancel a session based on its sessionId. Package name is for logging only.
- */
- private void cancelSession(int sessionId, String packageName) {
- // Close if currently held open
- closeSession(packageName);
- // Remove local record
- mSessionInfoMap.remove(packageName);
- try {
- mPackageInstaller.abandonSession(sessionId);
- } catch (SecurityException se) {
- // The session no longer exists, so we can exit quietly.
- return;
- }
- }
-
- /**
- * Close a session if it happens to be held open.
- */
- private void closeSession(String packageName) {
- PackageInstaller.Session session = mOpenSessionMap.remove(packageName);
- if (session != null) {
- // Unfortunately close() is not idempotent. Try our best to make this safe.
- try {
- session.close();
- } catch (Exception e) {
- Log.w(TAG, "Unexpected error closing session for " + packageName + ": "
- + e.getMessage());
- }
- }
- }
-
- /**
- * Creates a commit callback for the package install that's underway. This will be called
- * some time after calling session.commit() (above).
- */
- private IntentSender getCommitCallback(final String packageName, final int sessionId,
- final InstallListener callback) {
- // Create a single-use broadcast receiver
- BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mContext.unregisterReceiver(this);
- handleCommitCallback(intent, packageName, sessionId, callback);
- }
- };
- // Create a matching intent-filter and register the receiver
- String action = ACTION_INSTALL_COMMIT + "." + packageName;
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(action);
- mContext.registerReceiver(broadcastReceiver, intentFilter,
- Context.RECEIVER_EXPORTED);
-
- // Create a matching PendingIntent and use it to generate the IntentSender
- Intent broadcastIntent = new Intent(action).setPackage(mContext.getPackageName());
- PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, packageName.hashCode(),
- broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT
- | PendingIntent.FLAG_MUTABLE);
- return pendingIntent.getIntentSender();
- }
-
- /**
- * Examine the extras to determine information about the package update/install, decode
- * the result, and call the appropriate callback.
- *
- * @param intent The intent, which the PackageInstaller will have added Extras to
- * @param packageName The package name we created the receiver for
- * @param sessionId The session Id we created the receiver for
- * @param callback The callback to report success/failure to
- */
- private void handleCommitCallback(Intent intent, String packageName, int sessionId,
- InstallListener callback) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Installation of " + packageName + " finished with extras "
- + intent.getExtras());
- }
- String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
- int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Integer.MIN_VALUE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- cancelSession(sessionId, packageName);
- callback.installSucceeded();
- } else if (status == -1 /*PackageInstaller.STATUS_USER_ACTION_REQUIRED*/) {
- // TODO - use the constant when the correct/final name is in the SDK
- // TODO This is unexpected, so we are treating as failure for now
- cancelSession(sessionId, packageName);
- callback.installFailed(InstallerConstants.ERROR_INSTALL_USER_ACTION_REQUIRED,
- "Unexpected: user action required");
- } else {
- cancelSession(sessionId, packageName);
- int errorCode = getPackageManagerErrorCode(status);
- Log.e(TAG, "Error " + errorCode + " while installing " + packageName + ": "
- + statusMessage);
- callback.installFailed(errorCode, null);
- }
- }
-
- private int getPackageManagerErrorCode(int status) {
- // This is a hack: because PackageInstaller now reports error codes
- // with small positive values, we need to remap them into a space
- // that is more compatible with the existing package manager error codes.
- // See https://sites.google.com/a/google.com/universal-store/documentation
- // /android-client/download-error-codes
- int errorCode;
- if (status == Integer.MIN_VALUE) {
- errorCode = InstallerConstants.ERROR_INSTALL_MALFORMED_BROADCAST;
- } else {
- errorCode = InstallerConstants.ERROR_PACKAGEINSTALLER_BASE - status;
- }
- return errorCode;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java
deleted file mode 100644
index 2c289b2a6f94..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2015 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.packageinstaller.wear;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-/**
- * Installation Util that contains a list of parameters that are needed for
- * installing/uninstalling.
- */
-public class WearPackageArgs {
- private static final String KEY_PACKAGE_NAME =
- "com.google.android.clockwork.EXTRA_PACKAGE_NAME";
- private static final String KEY_ASSET_URI =
- "com.google.android.clockwork.EXTRA_ASSET_URI";
- private static final String KEY_START_ID =
- "com.google.android.clockwork.EXTRA_START_ID";
- private static final String KEY_PERM_URI =
- "com.google.android.clockwork.EXTRA_PERM_URI";
- private static final String KEY_CHECK_PERMS =
- "com.google.android.clockwork.EXTRA_CHECK_PERMS";
- private static final String KEY_SKIP_IF_SAME_VERSION =
- "com.google.android.clockwork.EXTRA_SKIP_IF_SAME_VERSION";
- private static final String KEY_COMPRESSION_ALG =
- "com.google.android.clockwork.EXTRA_KEY_COMPRESSION_ALG";
- private static final String KEY_COMPANION_SDK_VERSION =
- "com.google.android.clockwork.EXTRA_KEY_COMPANION_SDK_VERSION";
- private static final String KEY_COMPANION_DEVICE_VERSION =
- "com.google.android.clockwork.EXTRA_KEY_COMPANION_DEVICE_VERSION";
- private static final String KEY_SHOULD_CHECK_GMS_DEPENDENCY =
- "com.google.android.clockwork.EXTRA_KEY_SHOULD_CHECK_GMS_DEPENDENCY";
- private static final String KEY_SKIP_IF_LOWER_VERSION =
- "com.google.android.clockwork.EXTRA_SKIP_IF_LOWER_VERSION";
-
- public static String getPackageName(Bundle b) {
- return b.getString(KEY_PACKAGE_NAME);
- }
-
- public static Bundle setPackageName(Bundle b, String packageName) {
- b.putString(KEY_PACKAGE_NAME, packageName);
- return b;
- }
-
- public static Uri getAssetUri(Bundle b) {
- return b.getParcelable(KEY_ASSET_URI);
- }
-
- public static Uri getPermUri(Bundle b) {
- return b.getParcelable(KEY_PERM_URI);
- }
-
- public static boolean checkPerms(Bundle b) {
- return b.getBoolean(KEY_CHECK_PERMS);
- }
-
- public static boolean skipIfSameVersion(Bundle b) {
- return b.getBoolean(KEY_SKIP_IF_SAME_VERSION);
- }
-
- public static int getCompanionSdkVersion(Bundle b) {
- return b.getInt(KEY_COMPANION_SDK_VERSION);
- }
-
- public static int getCompanionDeviceVersion(Bundle b) {
- return b.getInt(KEY_COMPANION_DEVICE_VERSION);
- }
-
- public static String getCompressionAlg(Bundle b) {
- return b.getString(KEY_COMPRESSION_ALG);
- }
-
- public static int getStartId(Bundle b) {
- return b.getInt(KEY_START_ID);
- }
-
- public static boolean skipIfLowerVersion(Bundle b) {
- return b.getBoolean(KEY_SKIP_IF_LOWER_VERSION, false);
- }
-
- public static Bundle setStartId(Bundle b, int startId) {
- b.putInt(KEY_START_ID, startId);
- return b;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java
deleted file mode 100644
index 02b9d298db0e..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2015 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.packageinstaller.wear;
-
-import android.annotation.TargetApi;
-import android.app.ActivityManager;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.List;
-
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-public class WearPackageIconProvider extends ContentProvider {
- private static final String TAG = "WearPackageIconProvider";
- public static final String AUTHORITY = "com.google.android.packageinstaller.wear.provider";
-
- private static final String REQUIRED_PERMISSION =
- "com.google.android.permission.INSTALL_WEARABLE_PACKAGES";
-
- /** MIME types. */
- public static final String ICON_TYPE = "vnd.android.cursor.item/cw_package_icon";
-
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- throw new UnsupportedOperationException("Query is not supported.");
- }
-
- @Override
- public String getType(Uri uri) {
- if (uri == null) {
- throw new IllegalArgumentException("URI passed in is null.");
- }
-
- if (AUTHORITY.equals(uri.getEncodedAuthority())) {
- return ICON_TYPE;
- }
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- throw new UnsupportedOperationException("Insert is not supported.");
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- if (uri == null) {
- throw new IllegalArgumentException("URI passed in is null.");
- }
-
- enforcePermissions(uri);
-
- if (ICON_TYPE.equals(getType(uri))) {
- final File file = WearPackageUtil.getIconFile(
- this.getContext().getApplicationContext(), getPackageNameFromUri(uri));
- if (file != null) {
- file.delete();
- }
- }
-
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException("Update is not supported.");
- }
-
- @Override
- public ParcelFileDescriptor openFile(
- Uri uri, @SuppressWarnings("unused") String mode) throws FileNotFoundException {
- if (uri == null) {
- throw new IllegalArgumentException("URI passed in is null.");
- }
-
- enforcePermissions(uri);
-
- if (ICON_TYPE.equals(getType(uri))) {
- final File file = WearPackageUtil.getIconFile(
- this.getContext().getApplicationContext(), getPackageNameFromUri(uri));
- if (file != null) {
- return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- }
- }
- return null;
- }
-
- public static Uri getUriForPackage(final String packageName) {
- return Uri.parse("content://" + AUTHORITY + "/icons/" + packageName + ".icon");
- }
-
- private String getPackageNameFromUri(Uri uri) {
- if (uri == null) {
- return null;
- }
- List<String> pathSegments = uri.getPathSegments();
- String packageName = pathSegments.get(pathSegments.size() - 1);
-
- if (packageName.endsWith(".icon")) {
- packageName = packageName.substring(0, packageName.lastIndexOf("."));
- }
- return packageName;
- }
-
- /**
- * Make sure the calling app is either a system app or the same app or has the right permission.
- * @throws SecurityException if the caller has insufficient permissions.
- */
- @TargetApi(Build.VERSION_CODES.BASE_1_1)
- private void enforcePermissions(Uri uri) {
- // Redo some of the permission check in {@link ContentProvider}. Just add an extra check to
- // allow System process to access this provider.
- Context context = getContext();
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final int myUid = android.os.Process.myUid();
-
- if (uid == myUid || isSystemApp(context, pid)) {
- return;
- }
-
- if (context.checkPermission(REQUIRED_PERMISSION, pid, uid) == PERMISSION_GRANTED) {
- return;
- }
-
- // last chance, check against any uri grants
- if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
- == PERMISSION_GRANTED) {
- return;
- }
-
- throw new SecurityException("Permission Denial: reading "
- + getClass().getName() + " uri " + uri + " from pid=" + pid
- + ", uid=" + uid);
- }
-
- /**
- * From the pid of the calling process, figure out whether this is a system app or not. We do
- * this by checking the application information corresponding to the pid and then checking if
- * FLAG_SYSTEM is set.
- */
- @TargetApi(Build.VERSION_CODES.CUPCAKE)
- private boolean isSystemApp(Context context, int pid) {
- // Get the Activity Manager Object
- ActivityManager aManager =
- (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- // Get the list of running Applications
- List<ActivityManager.RunningAppProcessInfo> rapInfoList =
- aManager.getRunningAppProcesses();
- for (ActivityManager.RunningAppProcessInfo rapInfo : rapInfoList) {
- if (rapInfo.pid == pid) {
- try {
- PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(
- rapInfo.pkgList[0], 0);
- if (pkgInfo != null && pkgInfo.applicationInfo != null &&
- (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- Log.d(TAG, pid + " is a system app.");
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Could not find package information.", e);
- return false;
- }
- }
- }
- return false;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
deleted file mode 100644
index ae0f4ece1c17..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
- * Copyright (C) 2015 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.packageinstaller.wear;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.VersionedPackage;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.Process;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-import androidx.annotation.Nullable;
-import com.android.packageinstaller.DeviceUtils;
-import com.android.packageinstaller.PackageUtil;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.common.EventResultPersister;
-import com.android.packageinstaller.common.UninstallEventReceiver;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Service that will install/uninstall packages. It will check for permissions and features as well.
- *
- * -----------
- *
- * Debugging information:
- *
- * Install Action example:
- * adb shell am startservice -a com.android.packageinstaller.wear.INSTALL_PACKAGE \
- * -d package://com.google.android.gms \
- * --eu com.google.android.clockwork.EXTRA_ASSET_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/wearable/com.google.android.gms/apk \
- * --es android.intent.extra.INSTALLER_PACKAGE_NAME com.google.android.gms \
- * --ez com.google.android.clockwork.EXTRA_CHECK_PERMS false \
- * --eu com.google.android.clockwork.EXTRA_PERM_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/permissions \
- * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
- *
- * Uninstall Action example:
- * adb shell am startservice -a com.android.packageinstaller.wear.UNINSTALL_PACKAGE \
- * -d package://com.google.android.gms \
- * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
- *
- * Retry GMS:
- * adb shell am startservice -a com.android.packageinstaller.wear.RETRY_GMS \
- * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
- */
-public class WearPackageInstallerService extends Service
- implements EventResultPersister.EventResultObserver {
- private static final String TAG = "WearPkgInstallerService";
-
- private static final String WEAR_APPS_CHANNEL = "wear_app_install_uninstall";
- private static final String BROADCAST_ACTION =
- "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
-
- private final int START_INSTALL = 1;
- private final int START_UNINSTALL = 2;
-
- private int mInstallNotificationId = 1;
- private final Map<String, Integer> mNotifIdMap = new ArrayMap<>();
- private final Map<Integer, UninstallParams> mServiceIdToParams = new HashMap<>();
-
- private class UninstallParams {
- public String mPackageName;
- public PowerManager.WakeLock mLock;
-
- UninstallParams(String packageName, PowerManager.WakeLock lock) {
- mPackageName = packageName;
- mLock = lock;
- }
- }
-
- private final class ServiceHandler extends Handler {
- public ServiceHandler(Looper looper) {
- super(looper);
- }
-
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case START_INSTALL:
- installPackage(msg.getData());
- break;
- case START_UNINSTALL:
- uninstallPackage(msg.getData());
- break;
- }
- }
- }
- private ServiceHandler mServiceHandler;
- private NotificationChannel mNotificationChannel;
- private static volatile PowerManager.WakeLock lockStatic = null;
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- HandlerThread thread = new HandlerThread("PackageInstallerThread",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
-
- mServiceHandler = new ServiceHandler(thread.getLooper());
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (!DeviceUtils.isWear(this)) {
- Log.w(TAG, "Not running on wearable.");
- finishServiceEarly(startId);
- return START_NOT_STICKY;
- }
-
- if (intent == null) {
- Log.w(TAG, "Got null intent.");
- finishServiceEarly(startId);
- return START_NOT_STICKY;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Got install/uninstall request " + intent);
- }
-
- Uri packageUri = intent.getData();
- if (packageUri == null) {
- Log.e(TAG, "No package URI in intent");
- finishServiceEarly(startId);
- return START_NOT_STICKY;
- }
-
- final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri);
- if (packageName == null) {
- Log.e(TAG, "Invalid package name in URI (expected package:<pkgName>): " + packageUri);
- finishServiceEarly(startId);
- return START_NOT_STICKY;
- }
-
- PowerManager.WakeLock lock = getLock(this.getApplicationContext());
- if (!lock.isHeld()) {
- lock.acquire();
- }
-
- Bundle intentBundle = intent.getExtras();
- if (intentBundle == null) {
- intentBundle = new Bundle();
- }
- WearPackageArgs.setStartId(intentBundle, startId);
- WearPackageArgs.setPackageName(intentBundle, packageName);
- Message msg;
- String notifTitle;
- if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) {
- msg = mServiceHandler.obtainMessage(START_INSTALL);
- notifTitle = getString(R.string.installing);
- } else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) {
- msg = mServiceHandler.obtainMessage(START_UNINSTALL);
- notifTitle = getString(R.string.uninstalling);
- } else {
- Log.e(TAG, "Unknown action : " + intent.getAction());
- finishServiceEarly(startId);
- return START_NOT_STICKY;
- }
- Pair<Integer, Notification> notifPair = buildNotification(packageName, notifTitle);
- startForeground(notifPair.first, notifPair.second);
- msg.setData(intentBundle);
- mServiceHandler.sendMessage(msg);
- return START_NOT_STICKY;
- }
-
- private void installPackage(Bundle argsBundle) {
- int startId = WearPackageArgs.getStartId(argsBundle);
- final String packageName = WearPackageArgs.getPackageName(argsBundle);
- final Uri assetUri = WearPackageArgs.getAssetUri(argsBundle);
- final Uri permUri = WearPackageArgs.getPermUri(argsBundle);
- boolean checkPerms = WearPackageArgs.checkPerms(argsBundle);
- boolean skipIfSameVersion = WearPackageArgs.skipIfSameVersion(argsBundle);
- int companionSdkVersion = WearPackageArgs.getCompanionSdkVersion(argsBundle);
- int companionDeviceVersion = WearPackageArgs.getCompanionDeviceVersion(argsBundle);
- String compressionAlg = WearPackageArgs.getCompressionAlg(argsBundle);
- boolean skipIfLowerVersion = WearPackageArgs.skipIfLowerVersion(argsBundle);
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Installing package: " + packageName + ", assetUri: " + assetUri +
- ",permUri: " + permUri + ", startId: " + startId + ", checkPerms: " +
- checkPerms + ", skipIfSameVersion: " + skipIfSameVersion +
- ", compressionAlg: " + compressionAlg + ", companionSdkVersion: " +
- companionSdkVersion + ", companionDeviceVersion: " + companionDeviceVersion +
- ", skipIfLowerVersion: " + skipIfLowerVersion);
- }
- final PackageManager pm = getPackageManager();
- File tempFile = null;
- PowerManager.WakeLock lock = getLock(this.getApplicationContext());
- boolean messageSent = false;
- try {
- PackageInfo existingPkgInfo = null;
- try {
- existingPkgInfo = pm.getPackageInfo(packageName,
- PackageManager.MATCH_ANY_USER | PackageManager.GET_PERMISSIONS);
- if (existingPkgInfo != null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Replacing package:" + packageName);
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore this exception. We could not find the package, will treat as a new
- // installation.
- }
- // TODO(28021618): This was left as a temp file due to the fact that this code is being
- // deprecated and that we need the bare minimum to continue working moving forward
- // If this code is used as reference, this permission logic might want to be
- // reworked to use a stream instead of a file so that we don't need to write a
- // file at all. Note that there might be some trickiness with opening a stream
- // for multiple users.
- ParcelFileDescriptor parcelFd = getContentResolver()
- .openFileDescriptor(assetUri, "r");
- tempFile = WearPackageUtil.getFileFromFd(WearPackageInstallerService.this,
- parcelFd, packageName, compressionAlg);
- if (tempFile == null) {
- Log.e(TAG, "Could not create a temp file from FD for " + packageName);
- return;
- }
- PackageInfo pkgInfo = PackageUtil.getPackageInfo(this, tempFile,
- PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS);
- if (pkgInfo == null) {
- Log.e(TAG, "Could not parse apk information for " + packageName);
- return;
- }
-
- if (!pkgInfo.packageName.equals(packageName)) {
- Log.e(TAG, "Wearable Package Name has to match what is provided for " +
- packageName);
- return;
- }
-
- ApplicationInfo appInfo = pkgInfo.applicationInfo;
- appInfo.sourceDir = tempFile.getPath();
- appInfo.publicSourceDir = tempFile.getPath();
- getLabelAndUpdateNotification(packageName,
- getString(R.string.installing_app, appInfo.loadLabel(pm)));
-
- List<String> wearablePerms = Arrays.asList(pkgInfo.requestedPermissions);
-
- // Log if the installed pkg has a higher version number.
- if (existingPkgInfo != null) {
- long longVersionCode = pkgInfo.getLongVersionCode();
- if (existingPkgInfo.getLongVersionCode() == longVersionCode) {
- if (skipIfSameVersion) {
- Log.w(TAG, "Version number (" + longVersionCode +
- ") of new app is equal to existing app for " + packageName +
- "; not installing due to versionCheck");
- return;
- } else {
- Log.w(TAG, "Version number of new app (" + longVersionCode +
- ") is equal to existing app for " + packageName);
- }
- } else if (existingPkgInfo.getLongVersionCode() > longVersionCode) {
- if (skipIfLowerVersion) {
- // Starting in Feldspar, we are not going to allow downgrades of any app.
- Log.w(TAG, "Version number of new app (" + longVersionCode +
- ") is lower than existing app ( "
- + existingPkgInfo.getLongVersionCode() +
- ") for " + packageName + "; not installing due to versionCheck");
- return;
- } else {
- Log.w(TAG, "Version number of new app (" + longVersionCode +
- ") is lower than existing app ( "
- + existingPkgInfo.getLongVersionCode() + ") for " + packageName);
- }
- }
-
- // Following the Android Phone model, we should only check for permissions for any
- // newly defined perms.
- if (existingPkgInfo.requestedPermissions != null) {
- for (int i = 0; i < existingPkgInfo.requestedPermissions.length; ++i) {
- // If the permission is granted, then we will not ask to request it again.
- if ((existingPkgInfo.requestedPermissionsFlags[i] &
- PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, existingPkgInfo.requestedPermissions[i] +
- " is already granted for " + packageName);
- }
- wearablePerms.remove(existingPkgInfo.requestedPermissions[i]);
- }
- }
- }
- }
-
- // Check that the wearable has all the features.
- boolean hasAllFeatures = true;
- for (FeatureInfo feature : pkgInfo.reqFeatures) {
- if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
- (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
- Log.e(TAG, "Wearable does not have required feature: " + feature +
- " for " + packageName);
- hasAllFeatures = false;
- }
- }
-
- if (!hasAllFeatures) {
- return;
- }
-
- // Check permissions on both the new wearable package and also on the already installed
- // wearable package.
- // If the app is targeting API level 23, we will also start a service in ClockworkHome
- // which will ultimately prompt the user to accept/reject permissions.
- if (checkPerms && !checkPermissions(pkgInfo, companionSdkVersion,
- companionDeviceVersion, permUri, wearablePerms, tempFile)) {
- Log.w(TAG, "Wearable does not have enough permissions.");
- return;
- }
-
- // Finally install the package.
- ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r");
- PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd,
- new PackageInstallListener(this, lock, startId, packageName));
-
- messageSent = true;
- Log.i(TAG, "Sent installation request for " + packageName);
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Could not find the file with URI " + assetUri, e);
- } finally {
- if (!messageSent) {
- // Some error happened. If the message has been sent, we can wait for the observer
- // which will finish the service.
- if (tempFile != null) {
- tempFile.delete();
- }
- finishService(lock, startId);
- }
- }
- }
-
- // TODO: This was left using the old PackageManager API due to the fact that this code is being
- // deprecated and that we need the bare minimum to continue working moving forward
- // If this code is used as reference, this logic should be reworked to use the new
- // PackageInstaller APIs similar to how installPackage was reworked
- private void uninstallPackage(Bundle argsBundle) {
- int startId = WearPackageArgs.getStartId(argsBundle);
- final String packageName = WearPackageArgs.getPackageName(argsBundle);
-
- PowerManager.WakeLock lock = getLock(this.getApplicationContext());
-
- UninstallParams params = new UninstallParams(packageName, lock);
- mServiceIdToParams.put(startId, params);
-
- final PackageManager pm = getPackageManager();
- try {
- PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0);
- getLabelAndUpdateNotification(packageName,
- getString(R.string.uninstalling_app, pkgInfo.applicationInfo.loadLabel(pm)));
-
- int uninstallId = UninstallEventReceiver.addObserver(this,
- EventResultPersister.GENERATE_NEW_ID, this);
-
- Intent broadcastIntent = new Intent(BROADCAST_ACTION);
- broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, uninstallId);
- broadcastIntent.putExtra(EventResultPersister.EXTRA_SERVICE_ID, startId);
- broadcastIntent.setPackage(getPackageName());
-
- PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
- broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT
- | PendingIntent.FLAG_MUTABLE);
-
- // Found package, send uninstall request.
- pm.getPackageInstaller().uninstall(
- new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
- PackageManager.DELETE_ALL_USERS,
- pendingIntent.getIntentSender());
-
- Log.i(TAG, "Sent delete request for " + packageName);
- } catch (IllegalArgumentException | PackageManager.NameNotFoundException e) {
- // Couldn't find the package, no need to call uninstall.
- Log.w(TAG, "Could not find package, not deleting " + packageName, e);
- finishService(lock, startId);
- } catch (EventResultPersister.OutOfIdsException e) {
- Log.e(TAG, "Fails to start uninstall", e);
- finishService(lock, startId);
- }
- }
-
- @Override
- public void onResult(int status, int legacyStatus, @Nullable String message, int serviceId) {
- if (mServiceIdToParams.containsKey(serviceId)) {
- UninstallParams params = mServiceIdToParams.get(serviceId);
- try {
- if (status == PackageInstaller.STATUS_SUCCESS) {
- Log.i(TAG, "Package " + params.mPackageName + " was uninstalled.");
- } else {
- Log.e(TAG, "Package uninstall failed " + params.mPackageName
- + ", returnCode " + legacyStatus);
- }
- } finally {
- finishService(params.mLock, serviceId);
- }
- }
- }
-
- private boolean checkPermissions(PackageInfo pkgInfo, int companionSdkVersion,
- int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
- File apkFile) {
- // Assumption: We are running on Android O.
- // If the Phone App is targeting M, all permissions may not have been granted to the phone
- // app. If the Wear App is then not targeting M, there may be permissions that are not
- // granted on the Phone app (by the user) right now and we cannot just grant it for the Wear
- // app.
- if (pkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
- // Install the app if Wear App is ready for the new perms model.
- return true;
- }
-
- if (!doesWearHaveUngrantedPerms(pkgInfo.packageName, permUri, wearablePermissions)) {
- // All permissions requested by the watch are already granted on the phone, no need
- // to do anything.
- return true;
- }
-
- // Log an error if Wear is targeting < 23 and phone is targeting >= 23.
- if (companionSdkVersion == 0 || companionSdkVersion >= Build.VERSION_CODES.M) {
- Log.e(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if "
- + "phone app is targeting at least 23, will continue.");
- }
-
- return false;
- }
-
- /**
- * Given a {@string packageName} corresponding to a phone app, query the provider for all the
- * perms that are granted.
- *
- * @return true if the Wear App has any perms that have not been granted yet on the phone side.
- * @return true if there is any error cases.
- */
- private boolean doesWearHaveUngrantedPerms(String packageName, Uri permUri,
- List<String> wearablePermissions) {
- if (permUri == null) {
- Log.e(TAG, "Permission URI is null");
- // Pretend there is an ungranted permission to avoid installing for error cases.
- return true;
- }
- Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
- if (permCursor == null) {
- Log.e(TAG, "Could not get the cursor for the permissions");
- // Pretend there is an ungranted permission to avoid installing for error cases.
- return true;
- }
-
- Set<String> grantedPerms = new HashSet<>();
- Set<String> ungrantedPerms = new HashSet<>();
- while(permCursor.moveToNext()) {
- // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and
- // verify their types.
- if (permCursor.getColumnCount() == 2
- && Cursor.FIELD_TYPE_STRING == permCursor.getType(0)
- && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) {
- String perm = permCursor.getString(0);
- Integer granted = permCursor.getInt(1);
- if (granted == 1) {
- grantedPerms.add(perm);
- } else {
- ungrantedPerms.add(perm);
- }
- }
- }
- permCursor.close();
-
- boolean hasUngrantedPerm = false;
- for (String wearablePerm : wearablePermissions) {
- if (!grantedPerms.contains(wearablePerm)) {
- hasUngrantedPerm = true;
- if (!ungrantedPerms.contains(wearablePerm)) {
- // This is an error condition. This means that the wearable has permissions that
- // are not even declared in its host app. This is a developer error.
- Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm
- + "\" that is not defined in the host application's manifest.");
- } else {
- Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm +
- "\" that is not granted in the host application.");
- }
- }
- }
- return hasUngrantedPerm;
- }
-
- /** Finishes the service after fulfilling obligation to call startForeground. */
- private void finishServiceEarly(int startId) {
- Pair<Integer, Notification> notifPair = buildNotification(
- getApplicationContext().getPackageName(), "");
- startForeground(notifPair.first, notifPair.second);
- finishService(null, startId);
- }
-
- private void finishService(PowerManager.WakeLock lock, int startId) {
- if (lock != null && lock.isHeld()) {
- lock.release();
- }
- stopSelf(startId);
- }
-
- private synchronized PowerManager.WakeLock getLock(Context context) {
- if (lockStatic == null) {
- PowerManager mgr =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- lockStatic = mgr.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName());
- lockStatic.setReferenceCounted(true);
- }
- return lockStatic;
- }
-
- private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
- private Context mContext;
- private PowerManager.WakeLock mWakeLock;
- private int mStartId;
- private String mApplicationPackageName;
- private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock,
- int startId, String applicationPackageName) {
- mContext = context;
- mWakeLock = wakeLock;
- mStartId = startId;
- mApplicationPackageName = applicationPackageName;
- }
-
- @Override
- public void installBeginning() {
- Log.i(TAG, "Package " + mApplicationPackageName + " is being installed.");
- }
-
- @Override
- public void installSucceeded() {
- try {
- Log.i(TAG, "Package " + mApplicationPackageName + " was installed.");
-
- // Delete tempFile from the file system.
- File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName);
- if (tempFile != null) {
- tempFile.delete();
- }
- } finally {
- finishService(mWakeLock, mStartId);
- }
- }
-
- @Override
- public void installFailed(int errorCode, String errorDesc) {
- Log.e(TAG, "Package install failed " + mApplicationPackageName
- + ", errorCode " + errorCode);
- finishService(mWakeLock, mStartId);
- }
- }
-
- private synchronized Pair<Integer, Notification> buildNotification(final String packageName,
- final String title) {
- int notifId;
- if (mNotifIdMap.containsKey(packageName)) {
- notifId = mNotifIdMap.get(packageName);
- } else {
- notifId = mInstallNotificationId++;
- mNotifIdMap.put(packageName, notifId);
- }
-
- if (mNotificationChannel == null) {
- mNotificationChannel = new NotificationChannel(WEAR_APPS_CHANNEL,
- getString(R.string.wear_app_channel), NotificationManager.IMPORTANCE_MIN);
- NotificationManager notificationManager = getSystemService(NotificationManager.class);
- notificationManager.createNotificationChannel(mNotificationChannel);
- }
- return new Pair<>(notifId, new Notification.Builder(this, WEAR_APPS_CHANNEL)
- .setSmallIcon(R.drawable.ic_file_download)
- .setContentTitle(title)
- .build());
- }
-
- private void getLabelAndUpdateNotification(String packageName, String title) {
- // Update notification since we have a label now.
- NotificationManager notificationManager = getSystemService(NotificationManager.class);
- Pair<Integer, Notification> notifPair = buildNotification(packageName, title);
- notificationManager.notify(notifPair.first, notifPair.second);
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
deleted file mode 100644
index 6a9145db9a06..000000000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2015 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.packageinstaller.wear;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Log;
-
-import org.tukaani.xz.LZMAInputStream;
-import org.tukaani.xz.XZInputStream;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class WearPackageUtil {
- private static final String TAG = "WearablePkgInstaller";
-
- private static final String COMPRESSION_LZMA = "lzma";
- private static final String COMPRESSION_XZ = "xz";
-
- public static File getTemporaryFile(Context context, String packageName) {
- try {
- File newFileDir = new File(context.getFilesDir(), "tmp");
- newFileDir.mkdirs();
- Os.chmod(newFileDir.getAbsolutePath(), 0771);
- File newFile = new File(newFileDir, packageName + ".apk");
- return newFile;
- } catch (ErrnoException e) {
- Log.e(TAG, "Failed to open.", e);
- return null;
- }
- }
-
- public static File getIconFile(final Context context, final String packageName) {
- try {
- File newFileDir = new File(context.getFilesDir(), "images/icons");
- newFileDir.mkdirs();
- Os.chmod(newFileDir.getAbsolutePath(), 0771);
- return new File(newFileDir, packageName + ".icon");
- } catch (ErrnoException e) {
- Log.e(TAG, "Failed to open.", e);
- return null;
- }
- }
-
- /**
- * In order to make sure that the Wearable Asset Manager has a reasonable apk that can be used
- * by the PackageManager, we will parse it before sending it to the PackageManager.
- * Unfortunately, ParsingPackageUtils needs a file to parse. So, we have to temporarily convert
- * the fd to a File.
- *
- * @param context
- * @param fd FileDescriptor to convert to File
- * @param packageName Name of package, will define the name of the file
- * @param compressionAlg Can be null. For ALT mode the APK will be compressed. We will
- * decompress it here
- */
- public static File getFileFromFd(Context context, ParcelFileDescriptor fd,
- String packageName, String compressionAlg) {
- File newFile = getTemporaryFile(context, packageName);
- if (fd == null || fd.getFileDescriptor() == null) {
- return null;
- }
- InputStream fr = new ParcelFileDescriptor.AutoCloseInputStream(fd);
- try {
- if (TextUtils.equals(compressionAlg, COMPRESSION_XZ)) {
- fr = new XZInputStream(fr);
- } else if (TextUtils.equals(compressionAlg, COMPRESSION_LZMA)) {
- fr = new LZMAInputStream(fr);
- }
- } catch (IOException e) {
- Log.e(TAG, "Compression was set to " + compressionAlg + ", but could not decode ", e);
- return null;
- }
-
- int nRead;
- byte[] data = new byte[1024];
- try {
- final FileOutputStream fo = new FileOutputStream(newFile);
- while ((nRead = fr.read(data, 0, data.length)) != -1) {
- fo.write(data, 0, nRead);
- }
- fo.flush();
- fo.close();
- Os.chmod(newFile.getAbsolutePath(), 0644);
- return newFile;
- } catch (IOException e) {
- Log.e(TAG, "Reading from Asset FD or writing to temp file failed ", e);
- return null;
- } catch (ErrnoException e) {
- Log.e(TAG, "Could not set permissions on file ", e);
- return null;
- } finally {
- try {
- fr.close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to close the file from FD ", e);
- }
- }
- }
-
- /**
- * @return com.google.com from expected formats like
- * Uri: package:com.google.com, package:/com.google.com, package://com.google.com
- */
- public static String getSanitizedPackageName(Uri packageUri) {
- String packageName = packageUri.getEncodedSchemeSpecificPart();
- if (packageName != null) {
- return packageName.replaceAll("^/+", "");
- }
- return packageName;
- }
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index a28cfeb4dc80..68167e1598e0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -1127,7 +1127,7 @@ final class SettingsState {
Slog.i(LOG_TAG, "[PERSIST END]");
}
} catch (Throwable t) {
- Slog.wtf(LOG_TAG, "Failed to write settings, restoring old file", t);
+ Slog.e(LOG_TAG, "Failed to write settings, restoring old file", t);
if (t instanceof IOException) {
if (t.getMessage().contains("Couldn't create directory")) {
if (DEBUG) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index fa0a1c4663b1..a31b53383014 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -33,7 +33,7 @@ import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -94,7 +94,7 @@ constructor(
return
}
- NotificationStack(
+ ConstrainedNotificationStack(
viewModel = viewModel,
modifier =
modifier.fillMaxWidth().thenIf(shouldUseSplitNotificationShade) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index cda43476610e..579e837a62d0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -36,12 +36,14 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
@@ -60,7 +62,6 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import com.android.compose.animation.scene.ElementKey
@@ -68,12 +69,12 @@ import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.height
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
-import com.android.systemui.notifications.ui.composable.Notifications.Form
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import kotlin.math.roundToInt
@@ -81,7 +82,8 @@ import kotlin.math.roundToInt
object Notifications {
object Elements {
val NotificationScrim = ElementKey("NotificationScrim")
- val NotificationPlaceholder = ElementKey("NotificationPlaceholder")
+ val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder")
+ val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder")
val ShelfSpace = ElementKey("ShelfSpace")
}
@@ -91,12 +93,6 @@ object Notifications {
const val EXPANSION_FOR_MAX_CORNER_RADIUS = 0.1f
const val EXPANSION_FOR_MAX_SCRIM_ALPHA = 0.3f
}
-
- enum class Form {
- HunFromTop,
- Stack,
- HunFromBottom,
- }
}
/**
@@ -109,24 +105,48 @@ fun SceneScope.HeadsUpNotificationSpace(
modifier: Modifier = Modifier,
isPeekFromBottom: Boolean = false,
) {
- NotificationPlaceholder(
- viewModel = viewModel,
- form = if (isPeekFromBottom) Form.HunFromBottom else Form.HunFromTop,
- modifier = modifier,
- )
+ val headsUpHeight = viewModel.headsUpHeight.collectAsState()
+
+ Element(
+ Notifications.Elements.HeadsUpNotificationPlaceholder,
+ modifier =
+ modifier
+ .height { headsUpHeight.value.roundToInt() }
+ .fillMaxWidth()
+ .debugBackground(viewModel, DEBUG_HUN_COLOR)
+ .onGloballyPositioned { coordinates: LayoutCoordinates ->
+ val boundsInWindow = coordinates.boundsInWindow()
+ debugLog(viewModel) {
+ "HUNS onGloballyPositioned:" +
+ " size=${coordinates.size}" +
+ " bounds=$boundsInWindow"
+ }
+ viewModel.onHeadsUpTopChanged(boundsInWindow.top)
+ }
+ ) {
+ content {}
+ }
}
/** Adds the space where notification stack should appear in the scene. */
@Composable
-fun SceneScope.NotificationStack(
+fun SceneScope.ConstrainedNotificationStack(
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
) {
- NotificationPlaceholder(
- viewModel = viewModel,
- form = Form.Stack,
- modifier = modifier,
- )
+ Box(
+ modifier =
+ modifier.onSizeChanged { viewModel.onConstrainedAvailableSpaceChanged(it.height) }
+ ) {
+ NotificationPlaceholder(
+ viewModel = viewModel,
+ modifier = Modifier.fillMaxSize(),
+ )
+ HeadsUpNotificationSpace(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.TopCenter),
+ )
+ }
}
/**
@@ -169,6 +189,9 @@ fun SceneScope.NotificationScrollingStack(
// entire height of the scrim is visible on screen.
val scrimOffset = remember { mutableStateOf(0f) }
+ // set the bounds to null when the scrim disappears
+ DisposableEffect(Unit) { onDispose { viewModel.onScrimBoundsChanged(null) } }
+
val minScrimTop = with(density) { ShadeHeader.Dimensions.CollapsedHeight.toPx() }
// The minimum offset for the scrim. The scrim is considered fully expanded when it
@@ -235,6 +258,22 @@ fun SceneScope.NotificationScrollingStack(
.let { scrimRounding.value.toRoundedCornerShape(it) }
clip = true
}
+ .onGloballyPositioned { coordinates ->
+ val boundsInWindow = coordinates.boundsInWindow()
+ debugLog(viewModel) {
+ "SCRIM onGloballyPositioned:" +
+ " size=${coordinates.size}" +
+ " bounds=$boundsInWindow"
+ }
+ viewModel.onScrimBoundsChanged(
+ ShadeScrimBounds(
+ left = boundsInWindow.left,
+ top = boundsInWindow.top,
+ right = boundsInWindow.right,
+ bottom = boundsInWindow.bottom,
+ )
+ )
+ }
) {
// Creates a cutout in the background scrim in the shape of the notifications scrim.
// Only visible when notif scrim alpha < 1, during shade expansion.
@@ -254,11 +293,10 @@ fun SceneScope.NotificationScrollingStack(
} else 1f
}
.background(MaterialTheme.colorScheme.surface)
- .debugBackground(viewModel, Color(0.5f, 0.5f, 0f, 0.2f))
+ .debugBackground(viewModel, DEBUG_BOX_COLOR)
) {
NotificationPlaceholder(
viewModel = viewModel,
- form = Form.Stack,
modifier =
Modifier.verticalNestedScrollToScene(
topBehavior = NestedScrollBehavior.EdgeWithPreview,
@@ -284,6 +322,7 @@ fun SceneScope.NotificationScrollingStack(
.height { (stackHeight.value + navBarHeight).roundToInt() },
)
}
+ HeadsUpNotificationSpace(viewModel = viewModel)
}
}
@@ -304,14 +343,10 @@ fun SceneScope.NotificationShelfSpace(
modifier
.element(key = Notifications.Elements.ShelfSpace)
.fillMaxWidth()
- .onSizeChanged { size: IntSize ->
- debugLog(viewModel) { "SHELF onSizeChanged: size=$size" }
- }
.onPlaced { coordinates: LayoutCoordinates ->
debugLog(viewModel) {
("SHELF onPlaced:" +
" size=${coordinates.size}" +
- " position=${coordinates.positionInWindow()}" +
" bounds=${coordinates.boundsInWindow()}")
}
}
@@ -326,32 +361,26 @@ fun SceneScope.NotificationShelfSpace(
@Composable
private fun SceneScope.NotificationPlaceholder(
viewModel: NotificationsPlaceholderViewModel,
- form: Form,
modifier: Modifier = Modifier,
) {
- val elementKey = Notifications.Elements.NotificationPlaceholder
Element(
- elementKey,
+ Notifications.Elements.NotificationStackPlaceholder,
modifier =
modifier
- .debugBackground(viewModel)
- .onSizeChanged { size: IntSize ->
- debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
- }
+ .debugBackground(viewModel, DEBUG_STACK_COLOR)
+ .onSizeChanged { size -> debugLog(viewModel) { "STACK onSizeChanged: size=$size" } }
.onGloballyPositioned { coordinates: LayoutCoordinates ->
- viewModel.onContentTopChanged(coordinates.positionInWindow().y)
+ val positionInWindow = coordinates.positionInWindow()
debugLog(viewModel) {
"STACK onGloballyPositioned:" +
" size=${coordinates.size}" +
- " position=${coordinates.positionInWindow()}" +
+ " position=$positionInWindow" +
" bounds=${coordinates.boundsInWindow()}"
}
- val boundsInWindow = coordinates.boundsInWindow()
- viewModel.onBoundsChanged(
- left = boundsInWindow.left,
- top = boundsInWindow.top,
- right = boundsInWindow.right,
- bottom = boundsInWindow.bottom,
+ // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
+ viewModel.onStackBoundsChanged(
+ top = positionInWindow.y,
+ bottom = positionInWindow.y + coordinates.size.height,
)
}
) {
@@ -388,7 +417,7 @@ private inline fun debugLog(
private fun Modifier.debugBackground(
viewModel: NotificationsPlaceholderViewModel,
- color: Color = DEBUG_COLOR,
+ color: Color,
): Modifier =
if (viewModel.isVisualDebuggingEnabled) {
background(color)
@@ -397,8 +426,8 @@ private fun Modifier.debugBackground(
}
fun ShadeScrimRounding.toRoundedCornerShape(radius: Dp): RoundedCornerShape {
- val topRadius = if (roundTop) radius else 0.dp
- val bottomRadius = if (roundBottom) radius else 0.dp
+ val topRadius = if (isTopRounded) radius else 0.dp
+ val bottomRadius = if (isBottomRounded) radius else 0.dp
return RoundedCornerShape(
topStart = topRadius,
topEnd = topRadius,
@@ -408,4 +437,6 @@ fun ShadeScrimRounding.toRoundedCornerShape(radius: Dp): RoundedCornerShape {
}
private const val TAG = "FlexiNotifs"
-private val DEBUG_COLOR = Color(1f, 0f, 0f, 0.2f)
+private val DEBUG_STACK_COLOR = Color(1f, 0f, 0f, 0.2f)
+private val DEBUG_HUN_COLOR = Color(0f, 0f, 1f, 0.2f)
+private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 5b9213a8f23c..244f48019dc5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.ui.composable
import android.view.ViewGroup
+import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
@@ -122,6 +123,13 @@ private fun SceneScope.QuickSettingsScene(
// TODO(b/280887232): implement the real UI.
Box(modifier = modifier.fillMaxSize()) {
val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
+
+ BackHandler(
+ enabled = isCustomizing,
+ ) {
+ viewModel.qsSceneAdapter.requestCloseCustomizer()
+ }
+
val collapsedHeaderHeight =
with(LocalDensity.current) { ShadeHeader.Dimensions.CollapsedHeight.roundToPx() }
val lifecycleOwner = LocalLifecycleOwner.current
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index 27c4ec125b59..1a9ad3c285e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -496,4 +496,16 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
runCurrent()
verify(qsImpl!!).setInSplitShade(true)
}
+
+ @Test
+ fun requestCloseCustomizer() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+
+ underTest.inflate(context)
+ runCurrent()
+
+ underTest.requestCloseCustomizer()
+ verify(qsImpl!!).closeCustomizer()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index ef385673c950..d9ab3b172d2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -133,18 +133,13 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
}
@Test
- fun destinationsCustomizing() =
+ fun destinationsCustomizing_noDestinations() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
val destinations by collectLastValue(underTest.destinationScenes)
qsFlexiglassAdapter.setCustomizing(true)
- assertThat(destinations)
- .isEqualTo(
- mapOf(
- Back to UserActionResult(Scenes.QuickSettings),
- )
- )
+ assertThat(destinations).isEmpty()
}
@Test
@@ -164,18 +159,13 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
}
@Test
- fun destinations_whenCustomizing_inSplitShade() =
+ fun destinations_whenCustomizing_inSplitShade_noDestinations() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, true)
val destinations by collectLastValue(underTest.destinationScenes)
qsFlexiglassAdapter.setCustomizing(true)
- assertThat(destinations)
- .isEqualTo(
- mapOf(
- Back to UserActionResult(Scenes.QuickSettings),
- )
- )
+ assertThat(destinations).isEmpty()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index 53522e276112..3c28c0e4d709 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -31,7 +31,9 @@ import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationStackAppearanceViewModel
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ViewPosition
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationScrollViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -57,27 +59,44 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
}
private val testScope = kosmos.testScope
private val placeholderViewModel by lazy { kosmos.notificationsPlaceholderViewModel }
- private val appearanceViewModel by lazy { kosmos.notificationStackAppearanceViewModel }
+ private val appearanceViewModel by lazy { kosmos.notificationScrollViewModel }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
@Test
fun updateBounds() =
testScope.runTest {
- val clipping by collectLastValue(appearanceViewModel.shadeScrimClipping)
-
- val top = 200f
- val left = 0f
- val bottom = 550f
- val right = 100f
- placeholderViewModel.onBoundsChanged(
- left = left,
- top = top,
- right = right,
- bottom = bottom
+ val radius = MutableStateFlow(32)
+ val viewPosition = MutableStateFlow(ViewPosition(0, 0))
+ val shape by collectLastValue(appearanceViewModel.shadeScrimShape(radius, viewPosition))
+
+ placeholderViewModel.onScrimBoundsChanged(
+ ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f)
)
- assertThat(clipping?.bounds)
- .isEqualTo(ShadeScrimBounds(left = left, top = top, right = right, bottom = bottom))
+ assertThat(shape)
+ .isEqualTo(
+ ShadeScrimShape(
+ bounds =
+ ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f),
+ topRadius = 32,
+ bottomRadius = 0
+ )
+ )
+
+ viewPosition.value = ViewPosition(200, 15)
+ radius.value = 24
+ placeholderViewModel.onScrimBoundsChanged(
+ ShadeScrimBounds(left = 210f, top = 200f, right = 300f, bottom = 550f)
+ )
+ assertThat(shape)
+ .isEqualTo(
+ ShadeScrimShape(
+ bounds =
+ ShadeScrimBounds(left = 10f, top = 185f, right = 100f, bottom = 535f),
+ topRadius = 24,
+ bottomRadius = 0
+ )
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
index dc928c49fbd3..50b77dcf9468 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
@@ -68,11 +68,11 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() {
kosmos.shadeRepository.setShadeMode(ShadeMode.Single)
assertThat(stackRounding)
- .isEqualTo(ShadeScrimRounding(roundTop = true, roundBottom = false))
+ .isEqualTo(ShadeScrimRounding(isTopRounded = true, isBottomRounded = false))
kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
assertThat(stackRounding)
- .isEqualTo(ShadeScrimRounding(roundTop = true, roundBottom = true))
+ .isEqualTo(ShadeScrimRounding(isTopRounded = true, isBottomRounded = true))
}
@Test(expected = IllegalStateException::class)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index d4a704919a49..1f0812da9fe9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -16,14 +16,10 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -40,23 +36,21 @@ class NotificationsPlaceholderViewModelTest : SysuiTestCase() {
private val underTest = kosmos.notificationsPlaceholderViewModel
@Test
- @DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun onBoundsChanged_setsNotificationContainerBounds() =
+ fun onBoundsChanged() =
kosmos.testScope.runTest {
- underTest.onBoundsChanged(left = 5f, top = 5f, right = 5f, bottom = 5f)
- val containerBounds by
- collectLastValue(kosmos.keyguardInteractor.notificationContainerBounds)
+ val bounds = ShadeScrimBounds(left = 5f, top = 15f, right = 25f, bottom = 35f)
+ underTest.onScrimBoundsChanged(bounds)
val stackBounds by
collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds)
- assertThat(containerBounds)
- .isEqualTo(NotificationContainerBounds(top = 5f, bottom = 5f))
- assertThat(stackBounds)
- .isEqualTo(ShadeScrimBounds(left = 5f, top = 5f, right = 5f, bottom = 5f))
+ assertThat(stackBounds).isEqualTo(bounds)
}
@Test
- fun onContentTopChanged_setsContentTop() {
- underTest.onContentTopChanged(padding = 5f)
- assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
- }
+ fun onStackBoundsChanged() =
+ kosmos.testScope.runTest {
+ underTest.onStackBoundsChanged(top = 5f, bottom = 500f)
+ assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
+ assertThat(kosmos.notificationStackAppearanceInteractor.stackBottom.value)
+ .isEqualTo(500f)
+ }
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
index 173d57b335f0..3b6b5a0db393 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -91,6 +91,7 @@
android:layout_height="wrap_content"
android:contentDescription="@string/keyguard_accessibility_password"
android:gravity="center_horizontal"
+ android:layout_gravity="center"
android:imeOptions="flagForceAscii|actionDone"
android:inputType="textPassword"
android:maxLength="500"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 909d4fca5018..5aac65396532 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -55,6 +55,7 @@
android:layout_height="wrap_content"
android:contentDescription="@string/keyguard_accessibility_password"
android:gravity="center"
+ android:layout_gravity="center"
android:singleLine="true"
android:textStyle="normal"
android:inputType="textPassword"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9d79e8713625..b25c3daa9407 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -403,6 +403,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (DeviceEntryUdfpsRefactor.isEnabled()) {
if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
mOverlay.updateOverlayParams(mOverlayParams);
+ } else {
+ redrawOverlay();
}
} else {
final boolean wasShowingAlternateBouncer =
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
index ce798ba3912f..7dd883b89db7 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
@@ -17,6 +17,7 @@
package com.android.systemui.common.ui.view
import android.view.View
+import kotlinx.coroutines.DisposableHandle
/**
* Set this view's [View#importantForAccessibility] to [View#IMPORTANT_FOR_ACCESSIBILITY_YES] or
@@ -43,3 +44,10 @@ inline fun <reified T : View> View.getNearestParent(): T? {
}
return null
}
+
+/** Adds a [View.OnLayoutChangeListener] and provides a [DisposableHandle] for teardown. */
+fun View.onLayoutChanged(onLayoutChanged: (v: View) -> Unit): DisposableHandle {
+ val listener = View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ -> onLayoutChanged(v) }
+ addOnLayoutChangeListener(listener)
+ return DisposableHandle { removeOnLayoutChangeListener(listener) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 68ea5d047e1b..141cca329419 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
@@ -69,9 +70,11 @@ constructor(
}
}
- scope.launch {
- sharedNotificationContainerViewModel.bounds.collect {
- logger.log(TAG, VERBOSE, "Notif: bounds", it)
+ if (!SceneContainerFlag.isEnabled) {
+ scope.launch {
+ sharedNotificationContainerViewModel.bounds.collect {
+ logger.log(TAG, VERBOSE, "Notif: bounds", it)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 1382468ce7d3..486320af45a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -34,6 +34,7 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launch
+import com.android.internal.policy.SystemBarUtils
import com.android.systemui.customization.R as customizationR
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
@@ -116,7 +117,7 @@ object KeyguardPreviewClockViewBinder {
constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
constrainHeight(R.id.lockscreen_clock_view_large, ConstraintSet.MATCH_CONSTRAINT)
val largeClockTopMargin =
- context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
+ SystemBarUtils.getStatusBarHeight(context) +
context.resources.getDimensionPixelSize(
customizationR.dimen.small_clock_padding_top
) +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 39df4c3182a2..42bd4aff1dc4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -47,6 +47,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isInvisible
+import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.animation.view.LaunchableImageView
@@ -539,7 +540,7 @@ constructor(
)
)
layoutParams.topMargin =
- KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) +
+ SystemBarUtils.getStatusBarHeight(previewContext) +
resources.getDimensionPixelSize(
com.android.systemui.customization.R.dimen.small_clock_padding_top
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index f6f3bb1431d6..c9251c7c5473 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.helper.widget.Layer
+import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.KeyguardClockSwitch.LARGE
import com.android.keyguard.KeyguardClockSwitch.SMALL
import com.android.systemui.customization.R as customizationR
@@ -165,7 +166,7 @@ constructor(
companion object {
fun getLargeClockTopMargin(context: Context): Int {
- return context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
+ return SystemBarUtils.getStatusBarHeight(context) +
context.resources.getDimensionPixelSize(
customizationR.dimen.small_clock_padding_top
) +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index a90634e24580..b57e3ecbe05b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import android.content.res.Resources
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.SettingsClockSize
@@ -89,14 +88,4 @@ constructor(
}
}
}
- companion object {
- fun getStatusBarHeight(resource: Resources): Int {
- var result = 0
- val resourceId: Int = resource.getIdentifier("status_bar_height", "dimen", "android")
- if (resourceId > 0) {
- result = resource.getDimensionPixelSize(resourceId)
- }
- return result
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 671050477042..8d5aeab54343 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -93,6 +93,9 @@ interface QSSceneAdapter {
val isQsFullyCollapsed: Boolean
get() = true
+ /** Request that the customizer be closed. Possibly animating it. */
+ fun requestCloseCustomizer()
+
sealed interface State {
val isVisible: Boolean
@@ -277,6 +280,10 @@ constructor(
bottomNavBarSize.emit(padding)
}
+ override fun requestCloseCustomizer() {
+ qsImpl.value?.closeCustomizer()
+ }
+
private fun QSImpl.applyState(state: QSSceneAdapter.State) {
setQsVisible(state.isVisible)
setExpanded(state.isVisible && state.expansion > 0f)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index c695d4c98308..62ed49150eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -20,7 +20,6 @@ import androidx.lifecycle.LifecycleOwner
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.FooterActionsController
@@ -47,7 +46,9 @@ constructor(
val destinationScenes =
qsSceneAdapter.isCustomizing.map { customizing ->
if (customizing) {
- mapOf<UserAction, UserActionResult>(Back to UserActionResult(Scenes.QuickSettings))
+ // TODO(b/332749288) Empty map so there are no back handlers and back can close
+ // customizer
+ emptyMap()
// TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
// while customizing
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
index c42fdf8e7b93..b24edd9beece 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -25,6 +25,7 @@ import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import com.android.keyguard.LockIconViewController;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import java.util.HashSet;
@@ -80,12 +81,14 @@ public class DebugDrawable extends Drawable {
mNotificationPanelViewController.getClockPositionResult()
.stackScrollerPadding),
Color.YELLOW, "calculatePanelHeightShade()");
- drawDebugInfo(canvas,
- (int) mQsController.calculateNotificationsTopPadding(
- mNotificationPanelViewController.isExpandingOrCollapsing(),
- mNotificationPanelViewController.getKeyguardNotificationStaticPadding(),
- mNotificationPanelViewController.getExpandedFraction()),
- Color.MAGENTA, "calculateNotificationsTopPadding()");
+ if (!SceneContainerFlag.isEnabled()) {
+ drawDebugInfo(canvas,
+ (int) mQsController.calculateNotificationsTopPadding(
+ mNotificationPanelViewController.isExpandingOrCollapsing(),
+ mNotificationPanelViewController.getKeyguardNotificationStaticPadding(),
+ mNotificationPanelViewController.getExpandedFraction()),
+ Color.MAGENTA, "calculateNotificationsTopPadding()");
+ }
drawDebugInfo(canvas, mNotificationPanelViewController.getClockPositionResult().clockY,
Color.GRAY, "mClockPositionResult.clockY");
drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index dcfd47b7b303..4660831b77af 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1588,7 +1588,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
* @param forceClockUpdate Should the clock be updated even when not on keyguard
*/
private void positionClockAndNotifications(boolean forceClockUpdate) {
- boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
+ boolean animate = !SceneContainerFlag.isEnabled()
+ && mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
boolean onKeyguard = isKeyguardShowing();
@@ -1675,7 +1676,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mClockPositionResult.clockX, mClockPositionResult.clockY);
}
- boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
+ boolean animate = !SceneContainerFlag.isEnabled()
+ && mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
if (!MigrateClocksToBlueprint.isEnabled()) {
@@ -2483,6 +2485,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
int getKeyguardNotificationStaticPadding() {
+ SceneContainerFlag.assertInLegacyMode();
if (!isKeyguardShowing()) {
return 0;
}
@@ -2524,12 +2527,14 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
void requestScrollerTopPaddingUpdate(boolean animate) {
- float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
- getKeyguardNotificationStaticPadding(), mExpandedFraction);
- if (MigrateClocksToBlueprint.isEnabled()) {
- mSharedNotificationContainerInteractor.setTopPosition(padding);
- } else {
- mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
+ if (!SceneContainerFlag.isEnabled()) {
+ float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
+ getKeyguardNotificationStaticPadding(), mExpandedFraction);
+ if (MigrateClocksToBlueprint.isEnabled()) {
+ mSharedNotificationContainerInteractor.setTopPosition(padding);
+ } else {
+ mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
+ }
}
if (isKeyguardShowing()
@@ -3964,7 +3969,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mShadeRepository.setLegacyShadeExpansion(mExpandedFraction);
mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
mExpansionDragDownAmountPx = h;
- mAmbientState.setExpansionFraction(mExpandedFraction);
+ if (!SceneContainerFlag.isEnabled()) {
+ mAmbientState.setExpansionFraction(mExpandedFraction);
+ }
onHeightUpdated(mExpandedHeight);
updateExpansionAndVisibility();
});
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 89e84133c783..fb32b9fce909 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -417,6 +417,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_SECURE;
}
+
+ if (state.bouncerShowing) {
+ mLpChanged.inputFeatures |= LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_TRACING;
+ } else {
+ mLpChanged.inputFeatures &= ~LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_TRACING;
+ }
}
protected boolean isDebuggable() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 35b40592a47e..2507507ce22e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -1371,6 +1371,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
@Override
public float calculateNotificationsTopPadding(boolean isShadeExpanding,
int keyguardNotificationStaticPadding, float expandedFraction) {
+ SceneContainerFlag.assertInLegacyMode();
float topPadding;
boolean keyguardShowing = mBarState == KEYGUARD;
if (mSplitShadeEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt
index b8250cc284bd..34629934e467 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
@@ -28,7 +27,6 @@ class QuickSettingsControllerSceneImpl
constructor(
private val shadeInteractor: ShadeInteractor,
private val qsSceneAdapter: QSSceneAdapter,
- private val qsContainerController: QSContainerController,
) : QuickSettingsController {
override val expanded: Boolean
@@ -43,7 +41,7 @@ constructor(
}
override fun closeQsCustomizer() {
- qsContainerController.setCustomizerShowing(false)
+ qsSceneAdapter.requestCloseCustomizer()
}
@Deprecated("specific to legacy split shade")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 2cb9f9acca26..29cdf0a1cb43 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -43,6 +43,7 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.StatusBarLocation
@@ -51,6 +52,7 @@ import com.android.systemui.statusbar.phone.TapAgainView
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.tuner.TunerService
+import dagger.Binds
import dagger.Module
import dagger.Provides
import javax.inject.Named
@@ -59,6 +61,14 @@ import javax.inject.Provider
/** Module for providing views related to the shade. */
@Module
abstract class ShadeViewProviderModule {
+
+ @Binds
+ @SysUISingleton
+ // TODO(b/277762009): Only allow this view's binder to inject the view.
+ abstract fun bindsNotificationScrollView(
+ notificationStackScrollLayout: NotificationStackScrollLayout
+ ): NotificationScrollView
+
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 3349345b1391..c4293291de51 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -345,9 +345,7 @@ public class ShadeCarrierGroupController {
}
}
- if (mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
- Log.d(TAG, "ignoring old pipeline callback because new mobile icon is enabled");
- } else {
+ if (!mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
for (int i = 0; i < SIM_SLOTS; i++) {
mCarrierGroups[i].updateState(mInfos[i], singleCarrier);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 5b9eb21bf5e9..0bb871b23d27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -742,7 +742,7 @@ public class AmbientState implements Dumpable {
pw.println("mHideSensitive=" + mHideSensitive);
pw.println("mShadeExpanded=" + mShadeExpanded);
pw.println("mClearAllInProgress=" + mClearAllInProgress);
- pw.println("mStatusBarState=" + mStatusBarState);
+ pw.println("mStatusBarState=" + StatusBarState.toString(mStatusBarState));
pw.println("mExpansionChanging=" + mExpansionChanging);
pw.println("mPanelFullWidth=" + mIsSmallScreen);
pw.println("mPulsing=" + mPulsing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6a7a5cd8f6c3..fedb88d29837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -113,6 +113,9 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape;
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -135,6 +138,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
@@ -143,7 +147,9 @@ import java.util.function.Consumer;
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
-public class NotificationStackScrollLayout extends ViewGroup implements Dumpable {
+public class NotificationStackScrollLayout
+ extends ViewGroup
+ implements Dumpable, NotificationScrollView {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -218,6 +224,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private final StackScrollAlgorithm mStackScrollAlgorithm;
private final AmbientState mAmbientState;
+ private final ScrollViewFields mScrollViewFields = new ScrollViewFields();
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
@@ -590,7 +597,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Override
public boolean isScrolledToTop() {
if (SceneContainerFlag.isEnabled()) {
- return mController.isPlaceholderScrolledToTop();
+ return mScrollViewFields.isScrolledToTop();
} else {
return mOwnScrollY == 0;
}
@@ -855,7 +862,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
drawDebugInfo(canvas, y, Color.MAGENTA,
/* label= */ "mContentHeight = " + y);
- drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY,
+ y = mRoundedRectClippingBottom;
+ drawDebugInfo(canvas, y, Color.DKGRAY,
/* label= */ "mRoundedRectClippingBottom) = " + y);
}
@@ -1131,6 +1139,48 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
+ @Override
+ public View asView() {
+ return this;
+ }
+
+ @Override
+ public void setScrolledToTop(boolean scrolledToTop) {
+ mScrollViewFields.setScrolledToTop(scrolledToTop);
+ }
+
+ @Override
+ public void setStackTop(float stackTop) {
+ mScrollViewFields.setStackTop(stackTop);
+ // TODO(b/332574413): replace the following with using stackTop
+ updateTopPadding(stackTop, isAddOrRemoveAnimationPending());
+ }
+
+ @Override
+ public void setStackBottom(float stackBottom) {
+ mScrollViewFields.setStackBottom(stackBottom);
+ }
+
+ @Override
+ public void setHeadsUpTop(float headsUpTop) {
+ mScrollViewFields.setHeadsUpTop(headsUpTop);
+ }
+
+ @Override
+ public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setSyntheticScrollConsumer(consumer);
+ }
+
+ @Override
+ public void setStackHeightConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setStackHeightConsumer(consumer);
+ }
+
+ @Override
+ public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setHeadsUpHeightConsumer(consumer);
+ }
+
/**
* @param listener to be notified after the location of Notification children might have
* changed.
@@ -1381,6 +1431,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mOnStackYChanged = onStackYChanged;
}
+ @Override
+ public void setExpandFraction(float expandFraction) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ final float oldFraction = mAmbientState.getExpansionFraction();
+ final boolean wasExpanding = oldFraction != 0f && oldFraction != 1f;
+ final boolean nowExpanding = expandFraction != 0f && expandFraction != 1f;
+
+ // need to enter 'expanding' state before handling the new expand fraction, and then
+ if (nowExpanding && !wasExpanding) {
+ onExpansionStarted();
+ mController.checkSnoozeLeavebehind();
+ }
+
+ // Update the expand progress between started/stopped events
+ mAmbientState.setExpansionFraction(expandFraction);
+ // TODO(b/332577544): don't convert to height which then converts to the fraction again
+ setExpandedHeight(expandFraction * getHeight());
+
+ // expansion stopped event requires that the expandFraction has already been updated
+ if (!nowExpanding && wasExpanding) {
+ setCheckForLeaveBehind(false);
+ onExpansionStopped();
+ }
+ }
+
/**
* Update the height of the panel.
*
@@ -2315,7 +2390,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
shelfIntrinsicHeight);
mIntrinsicContentHeight = height;
- mController.setIntrinsicContentHeight(mIntrinsicContentHeight);
+ mScrollViewFields.sendStackHeight(height);
// The topPadding can be bigger than the regular padding when qs is expanded, in that
// state the maxPanelHeight and the contentHeight should be bigger
@@ -3555,7 +3630,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected boolean isInsideQsHeader(MotionEvent ev) {
if (SceneContainerFlag.isEnabled()) {
- return ev.getY() < mController.getPlaceholderTop();
+ return ev.getY() < mScrollViewFields.getScrimClippingShape().getBounds().getTop();
}
mQsHeader.getBoundsOnScreen(mQsHeaderBound);
@@ -4088,7 +4163,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// to it so that it can scroll the stack and scrim accordingly.
if (SceneContainerFlag.isEnabled()) {
float diff = endPosition - layoutEnd;
- mController.sendSyntheticScrollToSceneFramework(diff);
+ mScrollViewFields.sendSyntheticScroll(diff);
}
setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
mDisallowScrollingInThisMotion = true;
@@ -4971,6 +5046,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
elapsedRealtime - mLastUpdateSidePaddingElapsedRealtime);
println(pw, "isSmallLandscapeLockscreenEnabled", mIsSmallLandscapeLockscreenEnabled);
mNotificationStackSizeCalculator.dump(pw, args);
+ mScrollViewFields.dump(pw);
});
pw.println();
pw.println("Contents:");
@@ -5511,8 +5587,40 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Set rounded rect clipping bounds on this view.
*/
+ @Override
+ public void setScrimClippingShape(@Nullable ShadeScrimShape shape) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ if (Objects.equals(mScrollViewFields.getScrimClippingShape(), shape)) return;
+ mScrollViewFields.setScrimClippingShape(shape);
+ mShouldUseRoundedRectClipping = shape != null;
+ mRoundedClipPath.reset();
+ if (shape != null) {
+ ShadeScrimBounds bounds = shape.getBounds();
+ mRoundedRectClippingLeft = (int) bounds.getLeft();
+ mRoundedRectClippingTop = (int) bounds.getTop();
+ mRoundedRectClippingRight = (int) bounds.getRight();
+ mRoundedRectClippingBottom = (int) bounds.getBottom();
+ mBgCornerRadii[0] = shape.getTopRadius();
+ mBgCornerRadii[1] = shape.getTopRadius();
+ mBgCornerRadii[2] = shape.getTopRadius();
+ mBgCornerRadii[3] = shape.getTopRadius();
+ mBgCornerRadii[4] = shape.getBottomRadius();
+ mBgCornerRadii[5] = shape.getBottomRadius();
+ mBgCornerRadii[6] = shape.getBottomRadius();
+ mBgCornerRadii[7] = shape.getBottomRadius();
+ mRoundedClipPath.addRoundRect(
+ bounds.getLeft(), bounds.getTop(), bounds.getRight(), bounds.getBottom(),
+ mBgCornerRadii, Path.Direction.CW);
+ }
+ invalidate();
+ }
+
+ /**
+ * Set rounded rect clipping bounds on this view.
+ */
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
int bottomRadius) {
+ SceneContainerFlag.assertInLegacyMode();
if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
&& mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
&& mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
@@ -5590,6 +5698,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Should we use rounded rect clipping
*/
private void updateUseRoundedRectClipping() {
+ if (SceneContainerFlag.isEnabled()) return;
// We don't want to clip notifications when QS is expanded, because incoming heads up on
// the bottom would be clipped otherwise
boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 59901aca0425..06479e5a8e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -83,6 +83,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
@@ -125,7 +126,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
-import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -189,7 +189,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
private final ShadeController mShadeController;
private final Provider<WindowRootView> mWindowRootView;
- private final NotificationStackAppearanceInteractor mStackAppearanceInteractor;
private final KeyguardMediaController mKeyguardMediaController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
@@ -742,7 +741,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
NotificationListViewBinder viewBinder,
ShadeController shadeController,
Provider<WindowRootView> windowRootView,
- NotificationStackAppearanceInteractor stackAppearanceInteractor,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
NotificationStackScrollLogger logger,
@@ -795,7 +793,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mSeenNotificationsInteractor = seenNotificationsInteractor;
mShadeController = shadeController;
mWindowRootView = windowRootView;
- mStackAppearanceInteractor = stackAppearanceInteractor;
mFeatureFlags = featureFlags;
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
@@ -1124,6 +1121,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
public boolean isAddOrRemoveAnimationPending() {
+ SceneContainerFlag.assertInLegacyMode();
return mView != null && mView.isAddOrRemoveAnimationPending();
}
@@ -1163,29 +1161,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
}
- /** Send internal notification expansion to the scene container framework. */
- public void sendSyntheticScrollToSceneFramework(Float delta) {
- mStackAppearanceInteractor.setSyntheticScroll(delta);
- }
-
- /** Get the y-coordinate of the top bound of the stack. */
- public float getPlaceholderTop() {
- return mStackAppearanceInteractor.getShadeScrimBounds().getValue().getTop();
- }
-
- /**
- * Returns whether the notification stack is scrolled to the top; i.e., it cannot be scrolled
- * down any further.
- */
- public boolean isPlaceholderScrolledToTop() {
- return mStackAppearanceInteractor.getScrolledToTop().getValue();
- }
-
- /** Set the intrinsic height of the stack content without additional padding. */
- public void setIntrinsicContentHeight(float intrinsicContentHeight) {
- mStackAppearanceInteractor.setStackHeight(intrinsicContentHeight);
- }
-
public void setIntrinsicPadding(int intrinsicPadding) {
mView.setIntrinsicPadding(intrinsicPadding);
}
@@ -1264,6 +1239,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
public void setScrollingEnabled(boolean enabled) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setScrollingEnabled(enabled);
}
@@ -1284,6 +1260,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
public void updateTopPadding(float qsHeight, boolean animate) {
+ SceneContainerFlag.assertInLegacyMode();
mView.updateTopPadding(qsHeight, animate);
}
@@ -1386,11 +1363,13 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
public void onExpansionStarted() {
+ SceneContainerFlag.assertInLegacyMode();
mView.onExpansionStarted();
checkSnoozeLeavebehind();
}
public void onExpansionStopped() {
+ SceneContainerFlag.assertInLegacyMode();
mView.setCheckForLeaveBehind(false);
mView.onExpansionStopped();
}
@@ -1519,6 +1498,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
public void setExpandedHeight(float expandedHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setExpandedHeight(expandedHeight);
}
@@ -1794,6 +1774,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
*/
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
int bottomRadius) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
new file mode 100644
index 000000000000..edac5ede1e91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.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.statusbar.notification.stack
+
+import android.util.IndentingPrintWriter
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.util.printSection
+import com.android.systemui.util.println
+import java.util.function.Consumer
+
+/**
+ * This is a state holder object used by [NSSL][NotificationStackScrollLayout] to contain states
+ * provided by the `NotificationScrollViewBinder` to the `NotificationScrollView`.
+ *
+ * Unlike AmbientState, no class other than NSSL should ever have access to this class in any way.
+ * These fields are effectively NSSL's private fields.
+ */
+class ScrollViewFields {
+ /** Used to produce the clipping path */
+ var scrimClippingShape: ShadeScrimShape? = null
+ /** Y coordinate in view pixels of the top of the notification stack */
+ var stackTop: Float = 0f
+ /**
+ * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
+ * must be.
+ */
+ var stackBottom: Float = 0f
+ /** Y coordinate in view pixels of the top of the HUN */
+ var headsUpTop: Float = 0f
+ /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */
+ var isScrolledToTop: Boolean = true
+
+ /**
+ * When internal NSSL expansion requires the stack to be scrolled (e.g. to keep an expanding
+ * notification in view), that scroll amount can be sent here and it will be handled by the
+ * placeholder
+ */
+ var syntheticScrollConsumer: Consumer<Float>? = null
+ /**
+ * Any time the stack height is recalculated, it should be updated here to be used by the
+ * placeholder
+ */
+ var stackHeightConsumer: Consumer<Float>? = null
+ /**
+ * Any time the heads up height is recalculated, it should be updated here to be used by the
+ * placeholder
+ */
+ var headsUpHeightConsumer: Consumer<Float>? = null
+
+ /** send the [syntheticScroll] to the [syntheticScrollConsumer], if present. */
+ fun sendSyntheticScroll(syntheticScroll: Float) =
+ syntheticScrollConsumer?.accept(syntheticScroll)
+ /** send the [stackHeight] to the [stackHeightConsumer], if present. */
+ fun sendStackHeight(stackHeight: Float) = stackHeightConsumer?.accept(stackHeight)
+ /** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
+ fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.printSection("StackViewStates") {
+ pw.println("scrimClippingShape", scrimClippingShape)
+ pw.println("stackTop", stackTop)
+ pw.println("stackBottom", stackBottom)
+ pw.println("headsUpTop", headsUpTop)
+ pw.println("isScrolledToTop", isScrolledToTop)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index 462547ea2385..b04737936166 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -27,8 +27,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
*/
@SysUISingleton
class NotificationPlaceholderRepository @Inject constructor() {
- /** The bounds of the notification shade scrim / container in the current scene. */
- val shadeScrimBounds = MutableStateFlow(ShadeScrimBounds())
+ /**
+ * The bounds of the notification shade scrim / container in the current scene.
+ *
+ * When `null`, clipping should not be applied to notifications.
+ */
+ val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null)
/**
* The y-coordinate in px of top of the contents of the notification stack. This value can be
@@ -44,7 +48,7 @@ class NotificationPlaceholderRepository @Inject constructor() {
val headsUpTop = MutableStateFlow(0f)
/** height made available to the notifications in the size-constrained mode of lock screen. */
- val constrainedAvailableSpace = MutableStateFlow(0f)
+ val constrainedAvailableSpace = MutableStateFlow(0)
/**
* Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index 938e43e4a2d4..8a9da69079d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -35,7 +35,7 @@ class NotificationViewHeightRepository @Inject constructor() {
val stackHeight = MutableStateFlow(0f)
/** The height in px of the current heads up notification. */
- val activeHeadsUpRowHeight = MutableStateFlow(0f)
+ val headsUpHeight = MutableStateFlow(0f)
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 32562f109252..a5b4f5f2bb4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -42,7 +42,7 @@ constructor(
shadeInteractor: ShadeInteractor,
) {
/** The bounds of the notification stack in the current scene. */
- val shadeScrimBounds: StateFlow<ShadeScrimBounds> =
+ val shadeScrimBounds: StateFlow<ShadeScrimBounds?> =
placeholderRepository.shadeScrimBounds.asStateFlow()
/**
@@ -59,8 +59,8 @@ constructor(
isExpandingFromHeadsUp,
) { shadeMode, isExpandingFromHeadsUp ->
ShadeScrimRounding(
- roundTop = !(shadeMode == ShadeMode.Split && isExpandingFromHeadsUp),
- roundBottom = shadeMode != ShadeMode.Single,
+ isTopRounded = !(shadeMode == ShadeMode.Split && isExpandingFromHeadsUp),
+ isBottomRounded = shadeMode != ShadeMode.Single,
)
}
.distinctUntilChanged()
@@ -72,15 +72,28 @@ constructor(
*/
val stackHeight: StateFlow<Float> = viewHeightRepository.stackHeight.asStateFlow()
+ /** The height in px of the contents of the HUN. */
+ val headsUpHeight: StateFlow<Float> = viewHeightRepository.headsUpHeight.asStateFlow()
+
/** The y-coordinate in px of top of the contents of the notification stack. */
val stackTop: StateFlow<Float> = placeholderRepository.stackTop.asStateFlow()
+ /** The y-coordinate in px of bottom of the contents of the notification stack. */
+ val stackBottom: StateFlow<Float> = placeholderRepository.stackBottom.asStateFlow()
+
+ /** The height of the keyguard's available space bounds */
+ val constrainedAvailableSpace: StateFlow<Int> =
+ placeholderRepository.constrainedAvailableSpace.asStateFlow()
+
/**
* Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
* further.
*/
val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow()
+ /** The y-coordinate in px of bottom of the contents of the HUN. */
+ val headsUpTop: StateFlow<Float> = placeholderRepository.headsUpTop.asStateFlow()
+
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
@@ -89,8 +102,8 @@ constructor(
val syntheticScroll: Flow<Float> = viewHeightRepository.syntheticScroll.asStateFlow()
/** Sets the position of the notification stack in the current scene. */
- fun setShadeScrimBounds(bounds: ShadeScrimBounds) {
- check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
+ fun setShadeScrimBounds(bounds: ShadeScrimBounds?) {
+ check(bounds == null || bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
placeholderRepository.shadeScrimBounds.value = bounds
}
@@ -99,9 +112,19 @@ constructor(
viewHeightRepository.stackHeight.value = height
}
+ /** Sets the height of heads up notification. */
+ fun setHeadsUpHeight(height: Float) {
+ viewHeightRepository.headsUpHeight.value = height
+ }
+
/** Sets the y-coord in px of the top of the contents of the notification stack. */
- fun setStackTop(startY: Float) {
- placeholderRepository.stackTop.value = startY
+ fun setStackTop(stackTop: Float) {
+ placeholderRepository.stackTop.value = stackTop
+ }
+
+ /** Sets the y-coord in px of the bottom of the contents of the notification stack. */
+ fun setStackBottom(stackBottom: Float) {
+ placeholderRepository.stackBottom.value = stackBottom
}
/** Sets whether the notification stack is scrolled to the top. */
@@ -113,4 +136,12 @@ constructor(
fun setSyntheticScroll(delta: Float) {
viewHeightRepository.syntheticScroll.value = delta
}
+
+ fun setConstrainedAvailableSpace(height: Int) {
+ placeholderRepository.constrainedAvailableSpace.value = height
+ }
+
+ fun setHeadsUpTop(headsUpTop: Float) {
+ placeholderRepository.headsUpTop.value = headsUpTop
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt
index 448127a55b89..7e3e724bfed9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt
@@ -29,4 +29,12 @@ data class ShadeScrimBounds(
) {
/** The current height of the notification container. */
val height: Float = bottom - top
+
+ operator fun minus(position: ViewPosition) =
+ ShadeScrimBounds(
+ left = left - position.left,
+ top = top - position.top,
+ right = right - position.left,
+ bottom = bottom - position.top,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt
index 621dd0c49f54..e86fd1c7cdc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack.shared.model
-/** Models the clipping rounded rectangle of the notification stack */
+/** Models the clipping rounded rectangle of the notification stack, where the rounding is binary */
data class ShadeScrimClipping(
val bounds: ShadeScrimBounds = ShadeScrimBounds(),
val rounding: ShadeScrimRounding = ShadeScrimRounding()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt
index 2fe265f35604..9d4231c8967b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.stack.shared.model
/** Models the corner rounds of the notification stack. */
data class ShadeScrimRounding(
/** Whether the top corners of the notification stack should be rounded. */
- val roundTop: Boolean = false,
+ val isTopRounded: Boolean = false,
/** Whether the bottom corners of the notification stack should be rounded. */
- val roundBottom: Boolean = false,
+ val isBottomRounded: Boolean = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt
new file mode 100644
index 000000000000..e6f0647a059d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.shared.model
+
+/** Models the clipping rounded rectangle of the notification stack, with corner radii in px */
+data class ShadeScrimShape(
+ val bounds: ShadeScrimBounds = ShadeScrimBounds(),
+ /** radius in px of the top corners */
+ val topRadius: Int,
+ /** radius in px of the bottom corners */
+ val bottomRadius: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt
new file mode 100644
index 000000000000..5d2b0ad2b0a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.shared.model
+
+/** An offset of view, used to adjust bounds. */
+data class ViewPosition(val left: Int = 0, val top: Int = 0)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
new file mode 100644
index 000000000000..ac00d3b27493
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.view
+
+import android.view.View
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import java.util.function.Consumer
+
+/**
+ * This view (interface) is the view which scrolls and positions the heads up notification and
+ * notification stack, but is otherwise agnostic to the content.
+ */
+interface NotificationScrollView {
+ /**
+ * Since this is an interface rather than a literal View, this provides cast-like access to the
+ * underlying view.
+ */
+ fun asView(): View
+
+ /** Set the clipping bounds used when drawing */
+ fun setScrimClippingShape(shape: ShadeScrimShape?)
+
+ /** set the y position in px of the top of the stack in this view's coordinates */
+ fun setStackTop(stackTop: Float)
+
+ /** set the y position in px of the bottom of the stack in this view's coordinates */
+ fun setStackBottom(stackBottom: Float)
+
+ /** set the y position in px of the top of the HUN in this view's coordinates */
+ fun setHeadsUpTop(headsUpTop: Float)
+
+ /** set whether the view has been scrolled all the way to the top */
+ fun setScrolledToTop(scrolledToTop: Boolean)
+
+ /** Set a consumer for synthetic scroll events */
+ fun setSyntheticScrollConsumer(consumer: Consumer<Float>?)
+ /** Set a consumer for stack height changed events */
+ fun setStackHeightConsumer(consumer: Consumer<Float>?)
+ /** Set a consumer for heads up height changed events */
+ fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
+
+ /** sets that scrolling is allowed */
+ fun setScrollingEnabled(enabled: Boolean)
+
+ /** sets the current expand fraction */
+ fun setExpandFraction(expandFraction: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
new file mode 100644
index 000000000000..a98717a743d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.common.ui.view.onLayoutChanged
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.stack.shared.model.ViewPosition
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.launchAndDispose
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/** Binds the [NotificationScrollView]. */
+@SysUISingleton
+class NotificationScrollViewBinder
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+ private val view: NotificationScrollView,
+ private val viewModel: NotificationScrollViewModel,
+ private val configuration: ConfigurationState,
+) : FlowDumperImpl(dumpManager) {
+
+ private val viewPosition = MutableStateFlow(ViewPosition()).dumpValue("viewPosition")
+ private val viewTopOffset = viewPosition.map { it.top }.distinctUntilChanged()
+
+ fun bindWhileAttached(): DisposableHandle {
+ return view.asView().repeatWhenAttached(mainImmediateDispatcher) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
+ }
+ }
+
+ suspend fun bind() = coroutineScope {
+ launchAndDispose {
+ viewPosition.value = view.asView().position
+ view.asView().onLayoutChanged { viewPosition.value = it.position }
+ }
+
+ launch {
+ viewModel.shadeScrimShape(scrimRadius, viewPosition).collect {
+ view.setScrimClippingShape(it)
+ }
+ }
+
+ launch { viewModel.stackTop.minusTopOffset().collect { view.setStackTop(it) } }
+ launch { viewModel.stackBottom.minusTopOffset().collect { view.setStackBottom(it) } }
+ launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+ launch { viewModel.headsUpTop.minusTopOffset().collect { view.setHeadsUpTop(it) } }
+ launch { viewModel.expandFraction.collect { view.setExpandFraction(it) } }
+ launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
+
+ launchAndDispose {
+ view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
+ view.setStackHeightConsumer(viewModel.stackHeightConsumer)
+ view.setHeadsUpHeightConsumer(viewModel.headsUpHeightConsumer)
+ DisposableHandle {
+ view.setSyntheticScrollConsumer(null)
+ view.setStackHeightConsumer(null)
+ view.setHeadsUpHeightConsumer(null)
+ }
+ }
+ }
+
+ /** Combine with the topOffset flow and subtract that value from this flow's value */
+ private fun Flow<Float>.minusTopOffset() =
+ combine(viewTopOffset) { y, topOffset -> y - topOffset }
+
+ /** flow of the scrim clipping radius */
+ private val scrimRadius: Flow<Int>
+ get() = configuration.getDimensionPixelOffset(R.dimen.notification_scrim_corner_radius)
+
+ /** Construct a [ViewPosition] from this view using [View.getLeft] and [View.getTop] */
+ private val View.position
+ get() = ViewPosition(left = left, top = top)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt
deleted file mode 100644
index d6d31db86e61..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt
+++ /dev/null
@@ -1,103 +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.
- */
-
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.notification.stack.AmbientState
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.launch
-
-/** Binds the NSSL/Controller/AmbientState to their ViewModel. */
-@SysUISingleton
-class NotificationStackViewBinder
-@Inject
-constructor(
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
- private val ambientState: AmbientState,
- private val view: NotificationStackScrollLayout,
- private val controller: NotificationStackScrollLayoutController,
- private val viewModel: NotificationStackAppearanceViewModel,
- private val configuration: ConfigurationState,
-) {
-
- fun bindWhileAttached(): DisposableHandle {
- return view.repeatWhenAttached(mainImmediateDispatcher) {
- repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
- }
- }
-
- suspend fun bind() = coroutineScope {
- launch {
- combine(viewModel.shadeScrimClipping, clipRadius, ::Pair).collect {
- (clipping, clipRadius) ->
- val (bounds, rounding) = clipping
- val viewLeft = controller.view.left
- val viewTop = controller.view.top
- controller.setRoundedClippingBounds(
- bounds.left.roundToInt() - viewLeft,
- bounds.top.roundToInt() - viewTop,
- bounds.right.roundToInt() - viewLeft,
- bounds.bottom.roundToInt() - viewTop,
- if (rounding.roundTop) clipRadius else 0,
- if (rounding.roundBottom) clipRadius else 0,
- )
- }
- }
-
- launch {
- viewModel.stackTop.collect {
- controller.updateTopPadding(it, controller.isAddOrRemoveAnimationPending)
- }
- }
-
- launch {
- var wasExpanding = false
- viewModel.expandFraction.collect { expandFraction ->
- val nowExpanding = expandFraction != 0f && expandFraction != 1f
- if (nowExpanding && !wasExpanding) {
- controller.onExpansionStarted()
- }
- ambientState.expansionFraction = expandFraction
- controller.expandedHeight = expandFraction * controller.view.height
- if (!nowExpanding && wasExpanding) {
- controller.onExpansionStopped()
- }
- wasExpanding = nowExpanding
- }
- }
-
- launch { viewModel.isScrollable.collect { controller.setScrollingEnabled(it) } }
- }
-
- private val clipRadius: Flow<Int>
- get() = configuration.getDimensionPixelOffset(R.dimen.notification_scrim_corner_radius)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 4a096a89848a..cfd19bacdc69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -49,7 +49,7 @@ constructor(
private val sceneContainerFlags: SceneContainerFlags,
private val controller: NotificationStackScrollLayoutController,
private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
- private val notificationStackViewBinder: NotificationStackViewBinder,
+ private val notificationScrollViewBinder: NotificationScrollViewBinder,
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
) {
@@ -162,7 +162,7 @@ constructor(
}
if (sceneContainerFlags.isEnabled()) {
- disposables += notificationStackViewBinder.bindWhileAttached()
+ disposables += notificationScrollViewBinder.bindWhileAttached()
}
controller.setOnHeightChangedRunnable { viewModel.notificationStackChanged() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 071127c03202..9483f3310316 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -26,17 +26,18 @@ import com.android.systemui.scene.shared.model.Scenes.Shade
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ViewPosition
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
@SysUISingleton
-class NotificationStackAppearanceViewModel
+class NotificationScrollViewModel
@Inject
constructor(
dumpManager: DumpManager,
@@ -80,16 +81,48 @@ constructor(
.dumpWhileCollecting("expandFraction")
/** The bounds of the notification stack in the current scene. */
- val shadeScrimClipping: Flow<ShadeScrimClipping> =
+ private val shadeScrimClipping: Flow<ShadeScrimClipping?> =
combine(
stackAppearanceInteractor.shadeScrimBounds,
stackAppearanceInteractor.shadeScrimRounding,
- ::ShadeScrimClipping
- )
+ ) { bounds, rounding ->
+ bounds?.let { ShadeScrimClipping(it, rounding) }
+ }
.dumpWhileCollecting("stackClipping")
+ fun shadeScrimShape(
+ cornerRadius: Flow<Int>,
+ viewPosition: Flow<ViewPosition>
+ ): Flow<ShadeScrimShape?> =
+ combine(shadeScrimClipping, cornerRadius, viewPosition) { clipping, radius, position ->
+ if (clipping == null) return@combine null
+ ShadeScrimShape(
+ bounds = clipping.bounds - position,
+ topRadius = radius.takeIf { clipping.rounding.isTopRounded } ?: 0,
+ bottomRadius = radius.takeIf { clipping.rounding.isBottomRounded } ?: 0
+ )
+ }
+ .dumpWhileCollecting("shadeScrimShape")
+
/** The y-coordinate in px of top of the contents of the notification stack. */
- val stackTop: StateFlow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
+ val stackTop: Flow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
+ /** The y-coordinate in px of bottom of the contents of the notification stack. */
+ val stackBottom: Flow<Float> = stackAppearanceInteractor.stackBottom.dumpValue("stackBottom")
+ /**
+ * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
+ * further.
+ */
+ val scrolledToTop: Flow<Boolean> =
+ stackAppearanceInteractor.scrolledToTop.dumpValue("scrolledToTop")
+ /** The y-coordinate in px of bottom of the contents of the HUN. */
+ val headsUpTop: Flow<Float> = stackAppearanceInteractor.headsUpTop.dumpValue("headsUpTop")
+
+ /** Receives the amount (px) that the stack should scroll due to internal expansion. */
+ val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll
+ /** Receives the height of the contents of the notification stack. */
+ val stackHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setStackHeight
+ /** Receives the height of the heads up notification. */
+ val headsUpHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setHeadsUpHeight
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 477f139bcd7d..b284179d42b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -26,8 +27,10 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
+import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
@@ -37,67 +40,72 @@ import kotlinx.coroutines.flow.Flow
class NotificationsPlaceholderViewModel
@Inject
constructor(
+ dumpManager: DumpManager,
private val interactor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
flags: SceneContainerFlags,
featureFlags: FeatureFlagsClassic,
private val keyguardInteractor: KeyguardInteractor,
-) {
+) : FlowDumperImpl(dumpManager) {
/** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
/** DEBUG: whether the debug logging should be output. */
val isDebugLoggingEnabled: Boolean = flags.isEnabled()
- /**
- * Notifies that the bounds of the notification placeholder have changed.
- *
- * @param top The position of the top of the container in its window coordinate system, in
- * pixels.
- * @param bottom The position of the bottom of the container in its window coordinate system, in
- * pixels.
- */
- fun onBoundsChanged(
- left: Float,
+ /** Notifies that the bounds of the notification scrim have changed. */
+ fun onScrimBoundsChanged(bounds: ShadeScrimBounds?) {
+ interactor.setShadeScrimBounds(bounds)
+ }
+
+ /** Notifies that the bounds of the notification placeholder have changed. */
+ fun onStackBoundsChanged(
top: Float,
- right: Float,
bottom: Float,
) {
keyguardInteractor.setNotificationContainerBounds(
NotificationContainerBounds(top = top, bottom = bottom)
)
- interactor.setShadeScrimBounds(
- ShadeScrimBounds(top = top, bottom = bottom, left = left, right = right)
- )
+ interactor.setStackTop(top)
+ interactor.setStackBottom(bottom)
+ }
+
+ /** Sets the available space */
+ fun onConstrainedAvailableSpaceChanged(height: Int) {
+ interactor.setConstrainedAvailableSpace(height)
+ }
+
+ fun onHeadsUpTopChanged(headsUpTop: Float) {
+ interactor.setHeadsUpTop(headsUpTop)
}
/** Corner rounding of the stack */
- val shadeScrimRounding: Flow<ShadeScrimRounding> = interactor.shadeScrimRounding
+ val shadeScrimRounding: Flow<ShadeScrimRounding> =
+ interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
/**
* The height in px of the contents of notification stack. Depending on the number of
* notifications, this can exceed the space available on screen to show notifications, at which
* point the notification stack should become scrollable.
*/
- val stackHeight = interactor.stackHeight
+ val stackHeight: StateFlow<Float> = interactor.stackHeight.dumpValue("stackHeight")
+
+ /** The height in px of the contents of the HUN. */
+ val headsUpHeight: StateFlow<Float> = interactor.headsUpHeight.dumpValue("headsUpHeight")
/**
* The amount [0-1] that the shade has been opened. At 0, the shade is closed; at 1, the shade
* is open.
*/
- val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
+ val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion.dumpValue("expandFraction")
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
* necessary to scroll up to keep expanding the notification.
*/
- val syntheticScroll: Flow<Float> = interactor.syntheticScroll
-
- /** Sets the y-coord in px of the top of the contents of the notification stack. */
- fun onContentTopChanged(padding: Float) {
- interactor.setStackTop(padding)
- }
+ val syntheticScroll: Flow<Float> =
+ interactor.syntheticScroll.dumpWhileCollecting("syntheticScroll")
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 9f576066cc98..692368de5aea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -60,7 +60,9 @@ import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransition
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
@@ -97,6 +99,7 @@ constructor(
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val shadeInteractor: ShadeInteractor,
+ private val notificationStackAppearanceInteractor: NotificationStackAppearanceInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
@@ -364,6 +367,10 @@ constructor(
initialValue = NotificationContainerBounds(),
)
.dumpValue("bounds")
+ get() {
+ /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode()
+ return field
+ }
/**
* Ensure view is visible when the shade/qs are expanded. Also, as QS is expanding, fade out
@@ -571,8 +578,11 @@ constructor(
.dumpWhileCollecting("translationX")
private val availableHeight: Flow<Float> =
- bounds
- .map { it.bottom - it.top }
+ if (SceneContainerFlag.isEnabled) {
+ notificationStackAppearanceInteractor.constrainedAvailableSpace.map { it.toFloat() }
+ } else {
+ bounds.map { it.bottom - it.top }
+ }
.distinctUntilChanged()
.dumpWhileCollecting("availableHeight")
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt
index 909a18be4b9e..97d957dde01b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt
@@ -17,8 +17,14 @@
package com.android.systemui.util.kotlin
import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
/**
* Suspends to keep getting updates until cancellation. Once cancelled, mark this as eligible for
@@ -42,3 +48,22 @@ suspend fun DisposableHandle.awaitCancellationThenDispose() {
dispose()
}
}
+
+/**
+ * This will [launch], run [onLaunch] to get a [DisposableHandle], and finally
+ * [awaitCancellationThenDispose][DisposableHandle.awaitCancellationThenDispose]. This can be used
+ * to structure self-disposing code which attaches listeners, for example in ViewBinders:
+ * ```
+ * suspend fun bind(view: MyView, viewModel: MyViewModel) = coroutineScope {
+ * launchAndDispose {
+ * view.setOnClickListener { viewModel.handleClick() }
+ * DisposableHandle { view.setOnClickListener(null) }
+ * }
+ * }
+ * ```
+ */
+inline fun CoroutineScope.launchAndDispose(
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ crossinline onLaunch: () -> DisposableHandle
+): Job = launch(context, start) { onLaunch().awaitCancellationThenDispose() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 2ca5aeffcc15..c47f0bcb3192 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -23,6 +23,7 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
+import com.android.internal.policy.SystemBarUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -70,7 +71,7 @@ class ClockSectionTest : SysuiTestCase() {
Utils.getStatusBarHeaderHeightKeyguard(context)
private val LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE =
- context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
+ SystemBarUtils.getStatusBarHeight(context) +
context.resources.getDimensionPixelSize(
com.android.systemui.customization.R.dimen.small_clock_padding_top
) +
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 02f2e16b9570..cf7c6f4e2174 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_TRACING;
import static com.google.common.truth.Truth.assertThat;
@@ -436,6 +437,10 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) != 0).isTrue();
+ assertThat(
+ (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_TRACING)
+ != 0)
+ .isTrue();
}
@Test
@@ -444,6 +449,10 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue();
+ assertThat(
+ (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_TRACING)
+ == 0)
+ .isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index cd8be573a2ac..912ecb340c3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -94,7 +94,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
-import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -161,7 +160,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
@Mock private ShadeController mShadeController;
@Mock private Provider<WindowRootView> mWindowRootView;
- @Mock private NotificationStackAppearanceInteractor mNotificationStackAppearanceInteractor;
private final StackStateLogger mStackLogger = new StackStateLogger(logcatLogBuffer(),
logcatLogBuffer());
private final NotificationStackScrollLogger mLogger = new NotificationStackScrollLogger(
@@ -1016,7 +1014,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mViewBinder,
mShadeController,
mWindowRootView,
- mNotificationStackAppearanceInteractor,
mKosmos.getInteractionJankMonitor(),
mStackLogger,
mLogger,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index 4d902fa35204..df08e4a78178 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -60,4 +60,8 @@ class FakeQSSceneAdapter(
override suspend fun applyBottomNavBarPadding(padding: Int) {
_navBarPadding.value = padding
}
+
+ override fun requestCloseCustomizer() {
+ _customizing.value = false
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
index bada2a61995d..10cc13697d96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
@@ -23,8 +23,8 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
-val Kosmos.notificationStackAppearanceViewModel by Fixture {
- NotificationStackAppearanceViewModel(
+val Kosmos.notificationScrollViewModel by Fixture {
+ NotificationScrollViewModel(
dumpManager = dumpManager,
stackAppearanceInteractor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 29faa58d674a..b2492110f849 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.dump.dumpManager
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
@@ -26,6 +27,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.notif
val Kosmos.notificationsPlaceholderViewModel by Fixture {
NotificationsPlaceholderViewModel(
+ dumpManager = dumpManager,
interactor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
flags = sceneContainerFlags,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index de0cc6590593..d2de835ad954 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -43,6 +43,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -55,6 +56,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
shadeInteractor = shadeInteractor,
+ notificationStackAppearanceInteractor = notificationStackAppearanceInteractor,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java
deleted file mode 100644
index c11c1bba25a7..000000000000
--- a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.platform.test.ravenwood.bivalenttest;
-
-import static org.junit.Assert.assertEquals;
-
-import android.util.ArrayMap;
-import android.util.Size;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Map;
-
-// Tests for calling simple Android APIs.
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodAndroidApiTest {
- @Test
- public void testArrayMapSimple() {
- final Map<String, String> map = new ArrayMap<>();
-
- map.put("key1", "value1");
- assertEquals("value1", map.get("key1"));
- }
-
- @Test
- public void testSizeSimple() {
- final var size = new Size(1, 2);
-
- assertEquals(2, size.getHeight());
- }
-}
diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
deleted file mode 100644
index 6f2465c406d3..000000000000
--- a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.platform.test.ravenwood.bivalenttest;
-
-import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodClassRule;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@DisabledOnRavenwood
-public class RavenwoodClassRuleDeviceOnlyTest {
- @ClassRule
- public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
-
- @Test
- public void testDeviceOnly() {
- Assert.assertFalse(RavenwoodRule.isOnRavenwood());
- }
-}
diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java
deleted file mode 100644
index 21b31d1ca3e8..000000000000
--- a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.platform.test.ravenwood.bivalenttest;
-
-import android.platform.test.ravenwood.RavenwoodClassRule;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-// TODO: atest RavenwoodBivalentTest_device fails with the following message.
-// `RUNNER ERROR: Instrumentation reported numtests=7 but only ran 6`
-// @android.platform.test.annotations.DisabledOnNonRavenwood
-// Figure it out and then make DisabledOnNonRavenwood support TYPEs as well.
-@Ignore
-public class RavenwoodClassRuleRavenwoodOnlyTest {
- @ClassRule
- public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
-
- @Test
- public void testRavenwoodOnly() {
- Assert.assertTrue(RavenwoodRule.isOnRavenwood());
- }
-}
diff --git a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
index 4ee9a9c94826..2cd585ff6c9c 100644
--- a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
+++ b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
@@ -40,7 +40,7 @@ public class RavenwoodTestRunnerValidationTest {
public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
public RavenwoodTestRunnerValidationTest() {
- Assume.assumeTrue(RavenwoodRule._$RavenwoodPrivate.isOptionalValidationEnabled());
+ Assume.assumeTrue(mRavenwood._ravenwood_private$isOptionalValidationEnabled());
// Because RavenwoodRule will throw this error before executing the test method,
// we can't do it in the test method itself.
// So instead, we initialize it here.
diff --git a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
index 9d47f3a6bc5d..8ca34bafc2c6 100644
--- a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
@@ -31,17 +31,13 @@ import java.lang.annotation.Target;
* which means if a test class has this annotation, you can't negate it in subclasses or
* on a per-method basis.
*
- * THIS ANNOTATION CANNOT BE ADDED TO CLASSES AT THIS PONINT.
- * See {@link com.android.platform.test.ravenwood.bivalenttest.RavenwoodClassRuleRavenwoodOnlyTest}
- * for the reason.
- *
* The {@code RAVENWOOD_RUN_DISABLED_TESTS} environmental variable won't work because it won't be
* propagated to the device. (We may support it in the future, possibly using a debug. sysprop.)
*
* @hide
*/
@Inherited
-@Target({ElementType.METHOD})
+@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisabledOnNonRavenwood {
/**
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
index f4b7ec360dbf..9a4d4886d6f0 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
@@ -25,7 +25,6 @@ import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInP
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.EnabledOnRavenwood;
-import org.junit.Assert;
import org.junit.Assume;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -42,16 +41,27 @@ import org.junit.runners.model.Statement;
public class RavenwoodClassRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
+ // No special treatment when running outside Ravenwood; run tests as-is
if (!IS_ON_RAVENWOOD) {
- // This should be "Assume", not Assert, but if we use assume here, the device side
- // test runner would complain.
- // See the TODO comment in RavenwoodClassRuleRavenwoodOnlyTest.
- Assert.assertTrue(shouldEnableOnDevice(description));
- } else if (ENABLE_PROBE_IGNORED) {
+ Assume.assumeTrue(shouldEnableOnDevice(description));
+ return base;
+ }
+
+ if (ENABLE_PROBE_IGNORED) {
Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
+ // Pass through to possible underlying RavenwoodRule for both environment
+ // configuration and handling method-level annotations
+ return base;
} else {
- Assume.assumeTrue(shouldEnableOnRavenwood(description));
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ Assume.assumeTrue(shouldEnableOnRavenwood(description));
+ // Pass through to possible underlying RavenwoodRule for both environment
+ // configuration and handling method-level annotations
+ base.evaluate();
+ }
+ };
}
- return base;
}
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index a2e8ec17bbf7..52ea3402fa62 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -28,6 +28,7 @@ import android.platform.test.annotations.DisabledOnNonRavenwood;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.EnabledOnRavenwood;
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.util.ArraySet;
import org.junit.Assume;
import org.junit.rules.TestRule;
@@ -277,9 +278,6 @@ public class RavenwoodRule implements TestRule {
return false;
}
}
- if (description.getTestClass().getAnnotation(DisabledOnNonRavenwood.class) != null) {
- return false;
- }
return true;
}
@@ -415,9 +413,10 @@ public class RavenwoodRule implements TestRule {
};
}
- public static class _$RavenwoodPrivate {
- public static boolean isOptionalValidationEnabled() {
- return ENABLE_OPTIONAL_VALIDATION;
- }
+ /**
+ * Do not use it outside ravenwood core classes.
+ */
+ public boolean _ravenwood_private$isOptionalValidationEnabled() {
+ return ENABLE_OPTIONAL_VALIDATION;
}
}
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index 2a85eb66f6c5..e13746ed13dd 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -30,5 +30,6 @@ java_library_static {
aconfig_declarations {
name: "backup_flags",
package: "com.android.server.backup",
+ container: "system",
srcs: ["flags.aconfig"],
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index 74adfe08dba7..d53f949753d8 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.backup"
+container: "system"
flag {
name: "enable_skipping_restore_launched_apps"
@@ -58,4 +59,4 @@ flag {
description: "Increase BMM logging coverage in restore at install flow."
bug: "331749778"
is_fixed_read_only: true
-} \ No newline at end of file
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 93243fca9dd2..06431fcd783b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1068,6 +1068,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override
public boolean hasCustomAudioInputSupport() throws RemoteException {
+ return hasCustomAudioInputSupportInternal();
+ }
+
+ private boolean hasCustomAudioInputSupportInternal() {
if (!Flags.vdmPublicApis()) {
return false;
}
@@ -1119,6 +1123,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if (mVirtualCameraController != null) {
mVirtualCameraController.dump(fout, indent);
}
+ fout.println(
+ indent + "hasCustomAudioInputSupport: " + hasCustomAudioInputSupportInternal());
}
// For display mirroring, we want to dispatch all key events to the source (default) display,
diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp
index af1200e4bdf8..0294ffe6e151 100644
--- a/services/core/java/com/android/server/am/Android.bp
+++ b/services/core/java/com/android/server/am/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "am_flags",
package: "com.android.server.am",
+ container: "system",
srcs: ["*.aconfig"],
}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index a656287dc3f9..b517631f35c0 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -9,7 +9,6 @@ mwachens@google.com
sudheersai@google.com
suprabh@google.com
varunshah@google.com
-kwekua@google.com
bookatz@google.com
jji@google.com
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index fd847f11157f..e1ccf4d2a362 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.am"
+container: "system"
flag {
name: "oomadjuster_correctness_rewrite"
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 656611a79595..77654d4a5413 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1517,8 +1517,9 @@ public class AudioDeviceBroker {
sendLMsgNoDelay(MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
}
- /*package*/ void postUpdatedAdiDeviceState(AdiDeviceState deviceState) {
- sendLMsgNoDelay(MSG_L_UPDATED_ADI_DEVICE_STATE, SENDMSG_QUEUE, deviceState);
+ /*package*/ void postUpdatedAdiDeviceState(AdiDeviceState deviceState, boolean initSA) {
+ sendILMsgNoDelay(
+ MSG_IL_UPDATED_ADI_DEVICE_STATE, SENDMSG_QUEUE, initSA ? 1 : 0, deviceState);
}
/*package*/ static final class CommunicationDeviceInfo {
@@ -2048,8 +2049,8 @@ public class AudioDeviceBroker {
}
} break;
- case MSG_L_UPDATED_ADI_DEVICE_STATE:
- mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj);
+ case MSG_IL_UPDATED_ADI_DEVICE_STATE:
+ mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj, msg.arg1 == 1);
break;
default:
@@ -2136,7 +2137,7 @@ public class AudioDeviceBroker {
private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56;
private static final int MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES = 57;
private static final int MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY = 58;
- private static final int MSG_L_UPDATED_ADI_DEVICE_STATE = 59;
+ private static final int MSG_IL_UPDATED_ADI_DEVICE_STATE = 59;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ebe9c636d6f6..f38b38154bc3 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -173,7 +173,7 @@ public class AudioDeviceInventory {
if (ads.getAudioDeviceCategory() != category && (userDefined
|| category != AUDIO_DEVICE_CATEGORY_UNKNOWN)) {
ads.setAudioDeviceCategory(category);
- mDeviceBroker.postUpdatedAdiDeviceState(ads);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads, false /*initSA*/);
mDeviceBroker.postPersistAudioDeviceSettings();
}
mDeviceBroker.postSynchronizeAdiDevicesInInventory(ads);
@@ -186,7 +186,7 @@ public class AudioDeviceInventory {
mDeviceInventory.put(ads.getDeviceId(), ads);
checkDeviceInventorySize_l();
- mDeviceBroker.postUpdatedAdiDeviceState(ads);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads, true /*initSA*/);
mDeviceBroker.postPersistAudioDeviceSettings();
}
}
@@ -216,7 +216,7 @@ public class AudioDeviceInventory {
checkDeviceInventorySize_l();
}
if (updatedCategory.get()) {
- mDeviceBroker.postUpdatedAdiDeviceState(deviceState);
+ mDeviceBroker.postUpdatedAdiDeviceState(deviceState, false /*initSA*/);
}
mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
}
@@ -318,7 +318,7 @@ public class AudioDeviceInventory {
}
ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
- mDeviceBroker.postUpdatedAdiDeviceState(ads2);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads2, false /*initSA*/);
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"synchronizeBleDeviceInInventory synced device pair ads1="
+ updatedDevice + " ads2=" + ads2).printLog(TAG));
@@ -339,7 +339,7 @@ public class AudioDeviceInventory {
}
ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
- mDeviceBroker.postUpdatedAdiDeviceState(ads2);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads2, false /*initSA*/);
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"synchronizeBleDeviceInInventory synced device pair ads1="
+ updatedDevice + " peer ads2=" + ads2).printLog(TAG));
@@ -364,7 +364,7 @@ public class AudioDeviceInventory {
}
ads.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
- mDeviceBroker.postUpdatedAdiDeviceState(ads);
+ mDeviceBroker.postUpdatedAdiDeviceState(ads, false /*initSA*/);
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"synchronizeDeviceProfilesInInventory synced device pair ads1="
+ updatedDevice + " ads2=" + ads).printLog(TAG));
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 40099581c68b..2a79e316ac2b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -11284,7 +11284,8 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState);
mDeviceBroker.postPersistAudioDeviceSettings();
- mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes());
+ mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes(),
+ false /* initState */);
mSoundDoseHelper.setAudioDeviceCategory(addr, internalType,
btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES);
}
@@ -11355,11 +11356,11 @@ public class AudioService extends IAudioService.Stub
/** Update the sound dose and spatializer state based on the new AdiDeviceState. */
@VisibleForTesting(visibility = PACKAGE)
- public void onUpdatedAdiDeviceState(AdiDeviceState deviceState) {
+ public void onUpdatedAdiDeviceState(AdiDeviceState deviceState, boolean initSA) {
if (deviceState == null) {
return;
}
- mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes());
+ mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes(), initSA);
mSoundDoseHelper.setAudioDeviceCategory(deviceState.getDeviceAddress(),
deviceState.getInternalDeviceType(),
deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_HEADPHONES);
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 3b5fa7f00891..38fa79f7f44a 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -295,11 +295,11 @@ public class SpatializerHelper {
// could have been called another time after boot in case of audioserver restart
addCompatibleAudioDevice(
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
- false /*forceEnable*/);
+ false /*forceEnable*/, false /*forceInit*/);
// not force-enabling as this device might already be in the device list
addCompatibleAudioDevice(
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""),
- false /*forceEnable*/);
+ false /*forceEnable*/, false /*forceInit*/);
} catch (RemoteException e) {
resetCapabilities();
} finally {
@@ -526,7 +526,7 @@ public class SpatializerHelper {
}
synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- addCompatibleAudioDevice(ada, true /*forceEnable*/);
+ addCompatibleAudioDevice(ada, true /*forceEnable*/, false /*forceInit*/);
}
/**
@@ -540,7 +540,7 @@ public class SpatializerHelper {
*/
@GuardedBy("this")
private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
- boolean forceEnable) {
+ boolean forceEnable, boolean forceInit) {
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
return;
}
@@ -548,6 +548,9 @@ public class SpatializerHelper {
final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
AdiDeviceState updatedDevice = null; // non-null on update.
if (deviceState != null) {
+ if (forceInit) {
+ initSAState(deviceState);
+ }
if (forceEnable && !deviceState.isSAEnabled()) {
updatedDevice = deviceState;
updatedDevice.setSAEnabled(true);
@@ -756,10 +759,10 @@ public class SpatializerHelper {
}
}
- synchronized void refreshDevice(@NonNull AudioDeviceAttributes ada) {
+ synchronized void refreshDevice(@NonNull AudioDeviceAttributes ada, boolean initState) {
final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
if (isAvailableForAdiDeviceState(deviceState)) {
- addCompatibleAudioDevice(ada, /*forceEnable=*/deviceState.isSAEnabled());
+ addCompatibleAudioDevice(ada, /*forceEnable=*/deviceState.isSAEnabled(), initState);
setHeadTrackerEnabled(deviceState.isHeadTrackerEnabled(), ada);
} else {
removeCompatibleAudioDevice(ada);
diff --git a/services/core/java/com/android/server/biometrics/Android.bp b/services/core/java/com/android/server/biometrics/Android.bp
index 6cbe4adbcdbe..ed5de0305863 100644
--- a/services/core/java/com/android/server/biometrics/Android.bp
+++ b/services/core/java/com/android/server/biometrics/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "biometrics_flags",
package: "com.android.server.biometrics",
+ container: "system",
srcs: [
"biometrics.aconfig",
],
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index b12d831ffe24..7a9491e44cd7 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.biometrics"
+container: "system"
flag {
name: "face_vhal_feature"
diff --git a/services/core/java/com/android/server/display/feature/Android.bp b/services/core/java/com/android/server/display/feature/Android.bp
index a0ead384c1d2..daf8832fd1d7 100644
--- a/services/core/java/com/android/server/display/feature/Android.bp
+++ b/services/core/java/com/android/server/display/feature/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "display_flags",
package: "com.android.server.display.feature.flags",
+ container: "system",
srcs: [
"*.aconfig",
],
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index d4319cc2b633..c68ef9bd4cce 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.display.feature.flags"
+container: "system"
# Important: Flags must be accessed through DisplayManagerFlags.
diff --git a/services/core/java/com/android/server/feature/Android.bp b/services/core/java/com/android/server/feature/Android.bp
index 067288d6650d..b0fbab6657b0 100644
--- a/services/core/java/com/android/server/feature/Android.bp
+++ b/services/core/java/com/android/server/feature/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "dropbox_flags",
package: "com.android.server.feature.flags",
+ container: "system",
srcs: [
"dropbox_flags.aconfig",
],
diff --git a/services/core/java/com/android/server/feature/dropbox_flags.aconfig b/services/core/java/com/android/server/feature/dropbox_flags.aconfig
index 14e964b26c6b..98978f03fae5 100644
--- a/services/core/java/com/android/server/feature/dropbox_flags.aconfig
+++ b/services/core/java/com/android/server/feature/dropbox_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.feature.flags"
+container: "system"
flag{
name: "enable_read_dropbox_permission"
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index e7f717aa6c3b..f27ade42a738 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -136,7 +136,7 @@ import java.util.Objects;
mBluetoothRouteController =
new BluetoothDeviceRoutesManager(
- mContext, btAdapter, this::rebuildAvailableRoutesAndNotify);
+ mContext, mHandler, btAdapter, this::rebuildAvailableRoutesAndNotify);
// Just build routes but don't notify. The caller may not expect the listener to be invoked
// before this constructor has finished executing.
rebuildAvailableRoutes();
@@ -204,23 +204,24 @@ import java.util.Objects;
Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId);
return;
}
- // TODO: b/329929065 - Push audio manager and bluetooth operations to the handler, so that
- // they don't run on a binder thread, so as to prevent possible deadlocks (these operations
- // may need system_server binder threads to complete).
- if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) {
- // By default, the last connected device is the active route so we don't need to apply a
- // routing audio policy.
- mBluetoothRouteController.activateBluetoothDeviceWithAddress(
- mediaRoute2InfoHolder.mMediaRoute2Info.getAddress());
- mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
- } else {
- AudioDeviceAttributes attr =
- new AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- mediaRoute2InfoHolder.mAudioDeviceInfoType,
- /* address= */ ""); // This is not a BT device, hence no address needed.
- mAudioManager.setPreferredDeviceForStrategy(mStrategyForMedia, attr);
- }
+ Runnable transferAction = getTransferActionForRoute(mediaRoute2InfoHolder);
+ Runnable guardedTransferAction =
+ () -> {
+ try {
+ transferAction.run();
+ } catch (Throwable throwable) {
+ // We swallow the exception to avoid crashing system_server, since this
+ // doesn't run on a binder thread.
+ Slog.e(
+ TAG,
+ "Unexpected exception while transferring to route id: " + routeId,
+ throwable);
+ mHandler.post(this::rebuildAvailableRoutesAndNotify);
+ }
+ };
+ // We post the transfer operation to the handler to avoid making these calls on a binder
+ // thread. See class javadoc for details.
+ mHandler.post(guardedTransferAction);
}
@RequiresPermission(
@@ -236,6 +237,28 @@ import java.util.Objects;
return true;
}
+ private Runnable getTransferActionForRoute(MediaRoute2InfoHolder mediaRoute2InfoHolder) {
+ if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) {
+ String deviceAddress = mediaRoute2InfoHolder.mMediaRoute2Info.getAddress();
+ return () -> {
+ // By default, the last connected device is the active route so we don't
+ // need to apply a routing audio policy.
+ mBluetoothRouteController.activateBluetoothDeviceWithAddress(deviceAddress);
+ mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
+ };
+
+ } else {
+ AudioDeviceAttributes deviceAttributes =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ mediaRoute2InfoHolder.mAudioDeviceInfoType,
+ /* address= */ ""); // This is not a BT device, hence no address needed.
+ return () ->
+ mAudioManager.setPreferredDeviceForStrategy(
+ mStrategyForMedia, deviceAttributes);
+ }
+ }
+
@RequiresPermission(
anyOf = {
Manifest.permission.MODIFY_AUDIO_ROUTING,
diff --git a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
index b881ef6195d5..8b65ea305ad8 100644
--- a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
+++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaRoute2Info;
+import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -77,26 +78,35 @@ import java.util.stream.Collectors;
@NonNull
private final Context mContext;
- @NonNull
- private final BluetoothAdapter mBluetoothAdapter;
+ @NonNull private final Handler mHandler;
+ @NonNull private final BluetoothAdapter mBluetoothAdapter;
@NonNull
private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
@NonNull
private final BluetoothProfileMonitor mBluetoothProfileMonitor;
- BluetoothDeviceRoutesManager(@NonNull Context context,
+ BluetoothDeviceRoutesManager(
+ @NonNull Context context,
+ @NonNull Handler handler,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
- this(context, bluetoothAdapter,
- new BluetoothProfileMonitor(context, bluetoothAdapter), listener);
+ this(
+ context,
+ handler,
+ bluetoothAdapter,
+ new BluetoothProfileMonitor(context, bluetoothAdapter),
+ listener);
}
@VisibleForTesting
- BluetoothDeviceRoutesManager(@NonNull Context context,
+ BluetoothDeviceRoutesManager(
+ @NonNull Context context,
+ @NonNull Handler handler,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
mContext = Objects.requireNonNull(context);
+ mHandler = handler;
mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
mBluetoothProfileMonitor = Objects.requireNonNull(bluetoothProfileMonitor);
mListener = Objects.requireNonNull(listener);
@@ -298,6 +308,26 @@ import java.util.stream.Collectors;
};
}
+ private void handleBluetoothAdapterStateChange(int state) {
+ if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) {
+ synchronized (BluetoothDeviceRoutesManager.this) {
+ mBluetoothRoutes.clear();
+ }
+ notifyBluetoothRoutesUpdated();
+ } else if (state == BluetoothAdapter.STATE_ON) {
+ updateBluetoothRoutes();
+
+ boolean shouldCallListener;
+ synchronized (BluetoothDeviceRoutesManager.this) {
+ shouldCallListener = !mBluetoothRoutes.isEmpty();
+ }
+
+ if (shouldCallListener) {
+ notifyBluetoothRoutesUpdated();
+ }
+ }
+ }
+
private static class BluetoothRouteInfo {
private BluetoothDevice mBtDevice;
private MediaRoute2Info mRoute;
@@ -308,23 +338,10 @@ import java.util.stream.Collectors;
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
- if (state == BluetoothAdapter.STATE_OFF
- || state == BluetoothAdapter.STATE_TURNING_OFF) {
- synchronized (BluetoothDeviceRoutesManager.this) {
- mBluetoothRoutes.clear();
- }
- notifyBluetoothRoutesUpdated();
- } else if (state == BluetoothAdapter.STATE_ON) {
- updateBluetoothRoutes();
-
- boolean shouldCallListener;
- synchronized (BluetoothDeviceRoutesManager.this) {
- shouldCallListener = !mBluetoothRoutes.isEmpty();
- }
-
- if (shouldCallListener) {
- notifyBluetoothRoutesUpdated();
- }
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> handleBluetoothAdapterStateChange(state));
+ } else {
+ handleBluetoothAdapterStateChange(state);
}
}
}
@@ -337,8 +354,16 @@ import java.util.stream.Collectors;
case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
case BluetoothDevice.ACTION_ALIAS_CHANGED:
- updateBluetoothRoutes();
- notifyBluetoothRoutesUpdated();
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(
+ () -> {
+ updateBluetoothRoutes();
+ notifyBluetoothRoutesUpdated();
+ });
+ } else {
+ updateBluetoothRoutes();
+ notifyBluetoothRoutesUpdated();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index aa71e054ddee..e50189b2e79d 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -189,12 +189,10 @@ class MediaRouter2ServiceImpl {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- if (!Flags.disableScreenOffBroadcastReceiver()) {
- IntentFilter screenOnOffIntentFilter = new IntentFilter();
- screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
- screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
- mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
- }
+ IntentFilter screenOnOffIntentFilter = new IntentFilter();
+ screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
+ screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
// Passing null package name to listen to all events.
mAppOpsManager.startWatchingMode(
@@ -3435,9 +3433,7 @@ class MediaRouter2ServiceImpl {
@NonNull
private static List<RouterRecord> getIndividuallyActiveRouters(
MediaRouter2ServiceImpl service, List<RouterRecord> allRouterRecords) {
- if (!Flags.disableScreenOffBroadcastReceiver()
- && !service.mPowerManager.isInteractive()
- && !Flags.enableScreenOffScanning()) {
+ if (!service.mPowerManager.isInteractive() && !Flags.enableScreenOffScanning()) {
return Collections.emptyList();
}
@@ -3453,9 +3449,7 @@ class MediaRouter2ServiceImpl {
private static boolean areManagersScanning(
MediaRouter2ServiceImpl service, List<ManagerRecord> managerRecords) {
- if (!Flags.disableScreenOffBroadcastReceiver()
- && !service.mPowerManager.isInteractive()
- && !Flags.enableScreenOffScanning()) {
+ if (!service.mPowerManager.isInteractive() && !Flags.enableScreenOffScanning()) {
return false;
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 4bdca29d3bd0..1a129cb080a8 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -53,6 +53,7 @@ import android.media.RoutingSessionInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -70,6 +71,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
+import com.android.media.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.pm.UserManagerInternal;
@@ -94,6 +96,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
implements Watchdog.Monitor {
private static final String TAG = "MediaRouterService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String WORKER_THREAD_NAME = "MediaRouterServiceThread";
/**
* Timeout in milliseconds for a selected route to transition from a disconnected state to a
@@ -126,7 +129,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
private final IAudioService mAudioService;
private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
- private final Handler mHandler = new Handler();
+ private final Handler mHandler;
private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();
@@ -142,7 +145,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
public MediaRouterService(Context context) {
- mLooper = Looper.getMainLooper();
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ HandlerThread handlerThread = new HandlerThread(WORKER_THREAD_NAME);
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ } else {
+ mLooper = Looper.myLooper();
+ }
+ mHandler = new Handler(mLooper);
mService2 = new MediaRouter2ServiceImpl(context, mLooper);
mContext = context;
Watchdog.getInstance().addMonitor(this);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 802acba2e412..8df38a8d565a 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -653,9 +653,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
return;
}
- // TODO: b/310145678 - Post this to mHandler once mHandler does not run on the main
- // thread.
- updateVolume();
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(SystemMediaRoute2Provider.this::updateVolume);
+ } else {
+ updateVolume();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/net/Android.bp b/services/core/java/com/android/server/net/Android.bp
index 71d8e6ba367e..3ac2d232dfc8 100644
--- a/services/core/java/com/android/server/net/Android.bp
+++ b/services/core/java/com/android/server/net/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "net_flags",
package: "com.android.server.net",
+ container: "system",
srcs: ["*.aconfig"],
}
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index 419665a0a5ab..d9491de52d87 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.net"
+container: "system"
flag {
name: "network_blocked_for_top_sleeping_and_above"
diff --git a/services/core/java/com/android/server/notification/Android.bp b/services/core/java/com/android/server/notification/Android.bp
index 9be43581aed5..d757470d86e9 100644
--- a/services/core/java/com/android/server/notification/Android.bp
+++ b/services/core/java/com/android/server/notification/Android.bp
@@ -10,6 +10,7 @@ java_aconfig_library {
aconfig_declarations {
name: "notification_flags",
package: "com.android.server.notification",
+ container: "system",
srcs: [
"flags.aconfig",
],
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index d4641c45b8a6..077ed5a72ae9 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.notification"
+container: "system"
flag {
name: "expire_bitmaps"
diff --git a/services/core/java/com/android/server/os/Android.bp b/services/core/java/com/android/server/os/Android.bp
index 565dc3b644ea..c5886708ad85 100644
--- a/services/core/java/com/android/server/os/Android.bp
+++ b/services/core/java/com/android/server/os/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "core_os_flags",
package: "com.android.server.os",
+ container: "system",
srcs: [
"*.aconfig",
],
diff --git a/services/core/java/com/android/server/os/core_os_flags.aconfig b/services/core/java/com/android/server/os/core_os_flags.aconfig
index cbc0d54046df..ae33df83e3aa 100644
--- a/services/core/java/com/android/server/os/core_os_flags.aconfig
+++ b/services/core/java/com/android/server/os/core_os_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.os"
+container: "system"
flag {
name: "proto_tombstone"
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 017cf55541fc..306f77d3d11e 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -25,6 +25,7 @@ import static com.android.server.pm.CrossProfileIntentFilter.FLAG_IS_PACKAGE_FOR
import android.content.Intent;
import android.hardware.usb.UsbManager;
+import android.nfc.NfcAdapter;
import android.provider.AlarmClock;
import android.provider.MediaStore;
@@ -361,6 +362,7 @@ public class DefaultCrossProfileIntentFiltersUtils {
.addCategory(Intent.CATEGORY_DEFAULT)
.build();
+
public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
List<DefaultCrossProfileIntentFilter> filters =
new ArrayList<DefaultCrossProfileIntentFilter>();
@@ -637,6 +639,33 @@ public class DefaultCrossProfileIntentFiltersUtils {
.addDataType("video/*")
.build();
+ /** NFC TAG intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter PARENT_TO_CLONE_NFC_TAG_DISCOVERED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(NfcAdapter.ACTION_TAG_DISCOVERED)
+ .build();
+
+ /** NFC TAG intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter PARENT_TO_CLONE_NFC_TECH_DISCOVERED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(NfcAdapter.ACTION_TECH_DISCOVERED)
+ .build();
+
+ /** NFC TAG intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter PARENT_TO_CLONE_NFC_NDEF_DISCOVERED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(NfcAdapter.ACTION_NDEF_DISCOVERED)
+ .build();
+
public static List<DefaultCrossProfileIntentFilter> getDefaultCloneProfileFilters() {
return Arrays.asList(
PARENT_TO_CLONE_SEND_ACTION,
@@ -652,7 +681,10 @@ public class DefaultCrossProfileIntentFiltersUtils {
CLONE_TO_PARENT_SMS_MMS,
CLONE_TO_PARENT_PHOTOPICKER_SELECTION,
CLONE_TO_PARENT_ACTION_PICK_IMAGES,
- CLONE_TO_PARENT_ACTION_PICK_IMAGES_WITH_DATA_TYPES
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES_WITH_DATA_TYPES,
+ PARENT_TO_CLONE_NFC_TAG_DISCOVERED,
+ PARENT_TO_CLONE_NFC_TECH_DISCOVERED,
+ PARENT_TO_CLONE_NFC_NDEF_DISCOVERED
);
}
diff --git a/services/core/java/com/android/server/policy/Android.bp b/services/core/java/com/android/server/policy/Android.bp
index fa55bf0a30e5..325b6cbf5f2c 100644
--- a/services/core/java/com/android/server/policy/Android.bp
+++ b/services/core/java/com/android/server/policy/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "policy_flags",
package: "com.android.server.policy",
+ container: "system",
srcs: ["*.aconfig"],
}
diff --git a/services/core/java/com/android/server/policy/window_policy_flags.aconfig b/services/core/java/com/android/server/policy/window_policy_flags.aconfig
index 2154a26acbaf..7914b949ad5f 100644
--- a/services/core/java/com/android/server/policy/window_policy_flags.aconfig
+++ b/services/core/java/com/android/server/policy/window_policy_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.policy"
+container: "system"
flag {
name: "support_input_wakeup_delegate"
diff --git a/services/core/java/com/android/server/power/Android.bp b/services/core/java/com/android/server/power/Android.bp
index 863ff76cb0c7..5d4065df91d6 100644
--- a/services/core/java/com/android/server/power/Android.bp
+++ b/services/core/java/com/android/server/power/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "backstage_power_flags",
package: "com.android.server.power.optimization",
+ container: "system",
srcs: [
"stats/*.aconfig",
],
diff --git a/services/core/java/com/android/server/power/batterysaver/OWNERS b/services/core/java/com/android/server/power/batterysaver/OWNERS
index cf23bea97289..dc2d0b320d58 100644
--- a/services/core/java/com/android/server/power/batterysaver/OWNERS
+++ b/services/core/java/com/android/server/power/batterysaver/OWNERS
@@ -1,3 +1,2 @@
-kwekua@google.com
omakoto@google.com
-yamasani@google.com \ No newline at end of file
+yamasani@google.com
diff --git a/services/core/java/com/android/server/power/feature/Android.bp b/services/core/java/com/android/server/power/feature/Android.bp
index 2295b41009de..fee3114015a6 100644
--- a/services/core/java/com/android/server/power/feature/Android.bp
+++ b/services/core/java/com/android/server/power/feature/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "power_flags",
package: "com.android.server.power.feature.flags",
+ container: "system",
srcs: [
"*.aconfig",
],
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index f5dfb5cc3afe..ca58153cf25b 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.power.feature.flags"
+container: "system"
# Important: Flags must be accessed through PowerManagerFlags.
diff --git a/services/core/java/com/android/server/power/hint/Android.bp b/services/core/java/com/android/server/power/hint/Android.bp
index 8a98de673c3d..d7dd902dbabc 100644
--- a/services/core/java/com/android/server/power/hint/Android.bp
+++ b/services/core/java/com/android/server/power/hint/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "power_hint_flags",
package: "com.android.server.power.hint",
+ container: "system",
srcs: [
"flags.aconfig",
],
diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig
index f4afcb141b19..099774420d23 100644
--- a/services/core/java/com/android/server/power/hint/flags.aconfig
+++ b/services/core/java/com/android/server/power/hint/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.power.hint"
+container: "system"
flag {
name: "powerhint_thread_cleanup"
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index c42cceab55be..54ae84f4535b 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.power.optimization"
+container: "system"
flag {
name: "power_monitor_api"
diff --git a/services/core/java/com/android/server/stats/Android.bp b/services/core/java/com/android/server/stats/Android.bp
index e597c3a6b6e6..f7955e86660a 100644
--- a/services/core/java/com/android/server/stats/Android.bp
+++ b/services/core/java/com/android/server/stats/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "stats_flags",
package: "com.android.server.stats",
+ container: "system",
srcs: [
"stats_flags.aconfig",
],
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index 5101a6982fe1..101b98e1785d 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.stats"
+container: "system"
flag {
name: "add_mobile_bytes_transfer_by_proc_state_puller"
diff --git a/services/core/java/com/android/server/utils/Android.bp b/services/core/java/com/android/server/utils/Android.bp
index 3a334bee93ff..ffb9aec1cbef 100644
--- a/services/core/java/com/android/server/utils/Android.bp
+++ b/services/core/java/com/android/server/utils/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "com.android.server.utils-aconfig",
package: "com.android.server.utils",
+ container: "system",
srcs: ["*.aconfig"],
}
diff --git a/services/core/java/com/android/server/utils/flags.aconfig b/services/core/java/com/android/server/utils/flags.aconfig
index 163116b9c5c7..6f37837d4f3b 100644
--- a/services/core/java/com/android/server/utils/flags.aconfig
+++ b/services/core/java/com/android/server/utils/flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.utils"
+container: "system"
flag {
name: "anr_timer_service"
diff --git a/services/core/java/com/android/server/utils/quota/OWNERS b/services/core/java/com/android/server/utils/quota/OWNERS
index a2943f39db24..469acb23f0b9 100644
--- a/services/core/java/com/android/server/utils/quota/OWNERS
+++ b/services/core/java/com/android/server/utils/quota/OWNERS
@@ -1,4 +1,3 @@
dplotnikov@google.com
-kwekua@google.com
omakoto@google.com
yamasani@google.com
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
index 6ad8d790485c..6393e11b7432 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
@@ -1,6 +1,7 @@
aconfig_declarations {
name: "device_state_flags",
package: "com.android.server.policy.feature.flags",
+ container: "system",
srcs: [
"device_state_flags.aconfig",
],
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
index 29e258cc90ff..21e33dd1b99a 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.policy.feature.flags"
+container: "system"
flag {
name: "enable_dual_display_blocking"
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index a918be2292af..8bdfc500fdc2 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -69,6 +69,13 @@ import java.util.Set;
public class AudioManagerRouteControllerTest {
private static final String FAKE_ROUTE_NAME = "fake name";
+
+ /**
+ * The number of milliseconds to wait for an asynchronous operation before failing an associated
+ * assertion.
+ */
+ private static final int ASYNC_CALL_TIMEOUTS_MS = 1000;
+
private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER =
createAudioDeviceInfo(
AudioSystem.DEVICE_OUT_SPEAKER, "name_builtin", /* address= */ null);
@@ -231,7 +238,7 @@ public class AudioManagerRouteControllerTest {
MediaRoute2Info builtInSpeakerRoute =
getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
mControllerUnderTest.transferTo(builtInSpeakerRoute.getId());
- verify(mMockAudioManager)
+ verify(mMockAudioManager, Mockito.timeout(ASYNC_CALL_TIMEOUTS_MS))
.setPreferredDeviceForStrategy(
mMediaAudioProductStrategy,
createAudioDeviceAttribute(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER));
@@ -239,7 +246,7 @@ public class AudioManagerRouteControllerTest {
MediaRoute2Info wiredHeadsetRoute =
getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET);
mControllerUnderTest.transferTo(wiredHeadsetRoute.getId());
- verify(mMockAudioManager)
+ verify(mMockAudioManager, Mockito.timeout(ASYNC_CALL_TIMEOUTS_MS))
.setPreferredDeviceForStrategy(
mMediaAudioProductStrategy,
createAudioDeviceAttribute(AudioDeviceInfo.TYPE_WIRED_HEADSET));
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index f80156021408..0eb8639bd005 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -1,5 +1,5 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
-per-file SensitiveContentProtectionManagerServiceTest.java = file:/core/java/android/permission/OWNERS
+per-file SensitiveContentProtectionManagerService* = file:/core/java/android/permission/OWNERS
per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 682569f1d9ab..697548cbe2b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -1111,16 +1111,9 @@ public class RescuePartyTest {
// mock properties in BootThreshold
try {
- if (Flags.recoverabilityDetection()) {
- mSpyBootThreshold = spy(watchdog.new BootThreshold(
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS,
- PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT));
- } else {
- mSpyBootThreshold = spy(watchdog.new BootThreshold(
+ mSpyBootThreshold = spy(watchdog.new BootThreshold(
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
- }
mCrashRecoveryPropertiesMap = new HashMap<>();
doAnswer((Answer<Integer>) invocationOnMock -> {
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index 506514469338..edee8cd217d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -34,6 +33,7 @@ import static org.mockito.Mockito.when;
import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
+import android.os.Process;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -79,7 +79,8 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
private static final String NOTIFICATION_PKG_1 = "com.android.server.notification.one";
private static final String NOTIFICATION_PKG_2 = "com.android.server.notification.two";
- private static final String EXEMPTED_SCREEN_RECORDER_PACKAGE = "test.screen.recorder.package";
+ private static final String SCREEN_RECORDER_PACKAGE = "test.screen.recorder.package";
+ private static final String EXEMPTED_SCREEN_RECORDER_PACKAGE = "exempt.screen.recorder.package";
private static final int NOTIFICATION_UID_1 = 5;
private static final int NOTIFICATION_UID_2 = 6;
@@ -281,10 +282,18 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
.getActiveNotifications();
}
+ private MediaProjectionInfo createMediaProjectionInfo() {
+ return new MediaProjectionInfo(SCREEN_RECORDER_PACKAGE, Process.myUserHandle(), null);
+ }
+
+ private MediaProjectionInfo createExemptMediaProjectionInfo() {
+ return new MediaProjectionInfo(
+ EXEMPTED_SCREEN_RECORDER_PACKAGE, Process.myUserHandle(), null);
+ }
+
@Test
public void mediaProjectionOnStart_verifyExemptedRecorderPackage() {
- MediaProjectionInfo mediaProjectionInfo = mock(MediaProjectionInfo.class);
- when(mediaProjectionInfo.getPackageName()).thenReturn(EXEMPTED_SCREEN_RECORDER_PACKAGE);
+ MediaProjectionInfo mediaProjectionInfo = createExemptMediaProjectionInfo();
mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
@@ -295,7 +304,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
public void mediaProjectionOnStart_onProjectionStart_setWmBlockedPackages() {
ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@@ -304,18 +313,18 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
public void mediaProjectionOnStart_noSensitiveNotifications_noBlockedPackages() {
setupNoSensitiveNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
public void mediaProjectionOnStart_noNotifications_noBlockedPackages() {
setupNoNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -323,7 +332,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
ArraySet<PackageInfo> expectedBlockedPackages =
setupMultipleSensitiveNotificationsFromSamePackageAndUid();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@@ -333,7 +342,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
ArraySet<PackageInfo> expectedBlockedPackages =
setupMultipleSensitiveNotificationsFromDifferentPackage();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@@ -343,7 +352,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
ArraySet<PackageInfo> expectedBlockedPackages =
setupMultipleSensitiveNotificationsFromDifferentUid();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@@ -352,7 +361,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
public void mediaProjectionOnStop_onProjectionEnd_clearWmBlockedPackages() {
setupSensitiveNotification();
- MediaProjectionInfo mediaProjectionInfo = mock(MediaProjectionInfo.class);
+ MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
Mockito.reset(mWindowManager);
@@ -365,7 +374,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
public void mediaProjectionOnStart_afterOnStop_onProjectionStart_setWmBlockedPackages() {
ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
- MediaProjectionInfo mediaProjectionInfo = mock(MediaProjectionInfo.class);
+ MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
Mockito.reset(mWindowManager);
@@ -381,9 +390,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
.when(mSensitiveContentProtectionManagerService.mNotificationListener)
.getActiveNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -392,9 +401,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
.when(mSensitiveContentProtectionManagerService.mNotificationListener)
.getCurrentRanking();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -403,9 +412,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
.when(mSensitiveContentProtectionManagerService.mNotificationListener)
.getCurrentRanking();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -416,9 +425,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
.when(mSensitiveContentProtectionManagerService.mNotificationListener)
.getCurrentRanking();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -426,7 +435,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
mockDisabledViaDevelopOption();
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
verifyZeroInteractions(mWindowManager);
}
@@ -447,8 +456,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- mMediaProjectionCallbackCaptor.getValue().onStop(mock(MediaProjectionInfo.class));
+ MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+ mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
@@ -461,7 +471,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
@@ -472,23 +482,23 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
@Test
public void nlsOnListenerConnected_noSensitiveNotifications_noBlockedPackages() {
setupNoSensitiveNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
public void nlsOnListenerConnected_noNotifications_noBlockedPackages() {
setupNoNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -496,7 +506,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
doReturn(null)
.when(mSensitiveContentProtectionManagerService.mNotificationListener)
@@ -504,7 +514,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -512,7 +522,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
doReturn(mRankingMap)
@@ -521,7 +531,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -530,7 +540,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
verifyZeroInteractions(mWindowManager);
@@ -553,8 +563,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- mMediaProjectionCallbackCaptor.getValue().onStop(mock(MediaProjectionInfo.class));
+ MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+ mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -568,7 +579,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -580,25 +591,25 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
@Test
public void nlsOnNotificationRankingUpdate_noSensitiveNotifications_noBlockedPackages() {
setupNoSensitiveNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationRankingUpdate(mRankingMap);
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
public void nlsOnNotificationRankingUpdate_noNotifications_noBlockedPackages() {
setupNoNotifications();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationRankingUpdate(mRankingMap);
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -606,13 +617,13 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationRankingUpdate(null);
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -620,7 +631,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
doReturn(mRankingMap)
@@ -630,7 +641,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationRankingUpdate(mRankingMap);
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -638,7 +649,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
doThrow(SecurityException.class)
@@ -648,7 +659,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationRankingUpdate(mRankingMap);
- verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ verifyZeroInteractions(mWindowManager);
}
@Test
@@ -657,7 +668,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationRankingUpdate(mRankingMap);
@@ -681,8 +692,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- mMediaProjectionCallbackCaptor.getValue().onStop(mock(MediaProjectionInfo.class));
+ MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+ mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -696,7 +708,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -712,7 +724,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -726,7 +738,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -740,7 +752,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
mSensitiveContentProtectionManagerService.mNotificationListener
@@ -754,7 +766,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
Mockito.reset(mWindowManager);
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
@@ -770,7 +782,7 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
setupSensitiveNotification();
- mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
mSensitiveContentProtectionManagerService.mNotificationListener
.onNotificationPosted(mNotification1, mRankingMap);
diff --git a/services/tests/selinux/AndroidTest.xml b/services/tests/selinux/AndroidTest.xml
new file mode 100644
index 000000000000..16d8e0709345
--- /dev/null
+++ b/services/tests/selinux/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?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.
+ 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 Selinux Frameworks Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="SelinuxFrameworksTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="SelinuxFrameworksTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.selinuxtests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index f1c1dc365b90..59f4d56b44f1 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
@@ -315,7 +316,7 @@ public class AudioDeviceBrokerTest {
mFakeBtDevice.getAddress()));
verify(mMockAudioService,
timeout(MAX_MESSAGE_HANDLING_DELAY_MS).times(0)).onUpdatedAdiDeviceState(
- eq(devState));
+ eq(devState), anyBoolean());
}
// metadata set
@@ -326,7 +327,7 @@ public class AudioDeviceBrokerTest {
mFakeBtDevice.getAddress()));
verify(mMockAudioService,
timeout(MAX_MESSAGE_HANDLING_DELAY_MS)).onUpdatedAdiDeviceState(
- any());
+ any(), anyBoolean());
}
} finally {
// reset the metadata device type
@@ -354,7 +355,7 @@ public class AudioDeviceBrokerTest {
verify(mMockAudioService,
timeout(MAX_MESSAGE_HANDLING_DELAY_MS).atLeast(1)).onUpdatedAdiDeviceState(
ArgumentMatchers.argThat(devState -> devState.getAudioDeviceCategory()
- == AudioManager.AUDIO_DEVICE_CATEGORY_OTHER));
+ == AudioManager.AUDIO_DEVICE_CATEGORY_OTHER), anyBoolean());
}
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection,
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 081da11f2aa8..489ef4444e1d 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -66,6 +66,7 @@ import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -220,43 +221,36 @@ public class CrashRecoveryTest {
RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(1);
- int bootCounter = 0;
+
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
- bootCounter += 1;
}
+
verify(rescuePartyObserver).executeBootLoopMitigation(1);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- bootCounter += 1;
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(2);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
- int bootLoopThreshold = PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD - bootCounter;
- for (int i = 0; i < bootLoopThreshold; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(3);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(4);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(4);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(5);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(5);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(6);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(6);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(7);
}
@@ -268,11 +262,11 @@ public class CrashRecoveryTest {
setUpRollbackPackageHealthObserver(watchdog);
verify(rollbackObserver, never()).executeBootLoopMitigation(1);
- int bootCounter = 0;
+
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
- bootCounter += 1;
}
+
verify(rollbackObserver).executeBootLoopMitigation(1);
verify(rollbackObserver, never()).executeBootLoopMitigation(2);
@@ -280,19 +274,16 @@ public class CrashRecoveryTest {
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH,
ROLLBACK_INFO_MANUAL));
- int bootLoopThreshold = PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD - bootCounter;
- for (int i = 0; i < bootLoopThreshold; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rollbackObserver).executeBootLoopMitigation(2);
verify(rollbackObserver, never()).executeBootLoopMitigation(3);
// Update the list of available rollbacks after executing bootloop mitigation once
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL));
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rollbackObserver, never()).executeBootLoopMitigation(3);
}
@@ -305,27 +296,21 @@ public class CrashRecoveryTest {
verify(rescuePartyObserver, never()).executeBootLoopMitigation(1);
verify(rollbackObserver, never()).executeBootLoopMitigation(1);
- int bootCounter = 0;
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
- bootCounter += 1;
}
verify(rescuePartyObserver).executeBootLoopMitigation(1);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
verify(rollbackObserver, never()).executeBootLoopMitigation(1);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- bootCounter += 1;
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(2);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
verify(rollbackObserver, never()).executeBootLoopMitigation(2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- bootCounter += 1;
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
verify(rollbackObserver).executeBootLoopMitigation(1);
verify(rollbackObserver, never()).executeBootLoopMitigation(2);
@@ -333,43 +318,46 @@ public class CrashRecoveryTest {
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH,
ROLLBACK_INFO_MANUAL));
- int bootLoopThreshold = PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD - bootCounter;
- for (int i = 0; i < bootLoopThreshold; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(3);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(4);
verify(rollbackObserver, never()).executeBootLoopMitigation(2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(4);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(5);
verify(rollbackObserver, never()).executeBootLoopMitigation(2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(5);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(6);
verify(rollbackObserver, never()).executeBootLoopMitigation(2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver, never()).executeBootLoopMitigation(6);
verify(rollbackObserver).executeBootLoopMitigation(2);
verify(rollbackObserver, never()).executeBootLoopMitigation(3);
// Update the list of available rollbacks after executing bootloop mitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL));
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; i++) {
- watchdog.noteBoot();
- }
+ watchdog.noteBoot();
+
verify(rescuePartyObserver).executeBootLoopMitigation(6);
verify(rescuePartyObserver, never()).executeBootLoopMitigation(7);
verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
+ Mockito.reset(rescuePartyObserver);
+
+ for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
+ watchdog.noteBoot();
+ }
+ verify(rescuePartyObserver).executeBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
}
RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) {
@@ -506,16 +494,9 @@ public class CrashRecoveryTest {
}
try {
- if (Flags.recoverabilityDetection()) {
- mSpyBootThreshold = spy(watchdog.new BootThreshold(
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS,
- PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT));
- } else {
- mSpyBootThreshold = spy(watchdog.new BootThreshold(
+ mSpyBootThreshold = spy(watchdog.new BootThreshold(
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
- }
doAnswer((Answer<Integer>) invocationOnMock -> {
String storedValue = mCrashRecoveryPropertiesMap
@@ -640,5 +621,16 @@ public class CrashRecoveryTest {
public long uptimeMillis() {
return mUpTimeMillis;
}
+ public void moveTimeForward(long milliSeconds) {
+ mUpTimeMillis += milliSeconds;
+ }
+ }
+
+ private void moveTimeForwardAndDispatch(long milliSeconds) {
+ // Exhaust all due runnables now which shouldn't be executed after time-leap
+ mTestLooper.dispatchAll();
+ mTestClock.moveTimeForward(milliSeconds);
+ mTestLooper.moveTimeForward(milliSeconds);
+ mTestLooper.dispatchAll();
}
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 4f27e06083ba..093923f3ed53 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -45,13 +45,13 @@ import android.os.test.TestLooper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.util.AtomicFile;
+import android.util.LongArrayQueue;
import android.util.Xml;
-import android.utils.LongArrayQueue;
-import android.utils.XmlUtils;
import androidx.test.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.PackageWatchdog.HealthCheckState;
@@ -1224,7 +1224,7 @@ public class PackageWatchdogTest {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
watchdog.registerHealthObserver(bootObserver);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD; i++) {
+ for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
assertThat(bootObserver.mitigatedBootLoop()).isTrue();
@@ -1262,22 +1262,6 @@ public class PackageWatchdogTest {
}
/**
- * Ensure that boot loop mitigation is not done when the number of boots does not meet the
- * threshold.
- */
- @Test
- public void testBootLoopDetection_doesNotMeetThresholdRecoverabilityHighImpact() {
- PackageWatchdog watchdog = createWatchdog();
- TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_80);
- watchdog.registerHealthObserver(bootObserver);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD - 1; i++) {
- watchdog.noteBoot();
- }
- assertThat(bootObserver.mitigatedBootLoop()).isFalse();
- }
-
- /**
* Ensure that boot loop mitigation is done for the observer with the lowest user impact
*/
@Test
@@ -1306,7 +1290,7 @@ public class PackageWatchdogTest {
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
watchdog.registerHealthObserver(bootObserver1);
watchdog.registerHealthObserver(bootObserver2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD; i++) {
+ for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
@@ -1349,9 +1333,7 @@ public class PackageWatchdogTest {
watchdog.noteBoot();
}
for (int i = 0; i < 4; i++) {
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; j++) {
watchdog.noteBoot();
- }
}
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
@@ -1360,38 +1342,7 @@ public class PackageWatchdogTest {
watchdog.noteBoot();
}
for (int i = 0; i < 4; i++) {
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; j++) {
watchdog.noteBoot();
- }
- }
-
- assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
- }
-
- @Test
- public void testMultipleBootLoopMitigationRecoverabilityHighImpact() {
- PackageWatchdog watchdog = createWatchdog();
- TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_80);
- watchdog.registerHealthObserver(bootObserver);
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD - 1; j++) {
- watchdog.noteBoot();
- }
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; j++) {
- watchdog.noteBoot();
- }
- }
-
- moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
-
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_THRESHOLD - 1; j++) {
- watchdog.noteBoot();
- }
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT; j++) {
- watchdog.noteBoot();
- }
}
assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
@@ -1642,8 +1593,7 @@ public class PackageWatchdogTest {
mSpyBootThreshold = spy(watchdog.new BootThreshold(
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS,
- PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT));
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
watchdog.saveAllObserversBootMitigationCountToMetadata(filePath);
@@ -1798,16 +1748,9 @@ public class PackageWatchdogTest {
mCrashRecoveryPropertiesMap = new HashMap<>();
try {
- if (Flags.recoverabilityDetection()) {
- mSpyBootThreshold = spy(watchdog.new BootThreshold(
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
- PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS,
- PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT));
- } else {
- mSpyBootThreshold = spy(watchdog.new BootThreshold(
+ mSpyBootThreshold = spy(watchdog.new BootThreshold(
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
- }
doAnswer((Answer<Integer>) invocationOnMock -> {
String storedValue = mCrashRecoveryPropertiesMap
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 682adbc86d06..30333da5e86c 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -82,30 +82,13 @@ java_library {
jarjar_rules: "jarjar-rules.txt",
}
-// For sharing the code with other tools
-java_library_host {
- name: "hoststubgen-lib",
- defaults: ["ravenwood-internal-only-visibility-java"],
- srcs: ["src/**/*.kt"],
- static_libs: [
- "hoststubgen-helper-runtime",
- ],
- libs: [
- "junit",
- "ow2-asm",
- "ow2-asm-analysis",
- "ow2-asm-commons",
- "ow2-asm-tree",
- "ow2-asm-util",
- ],
-}
-
// Host-side stub generator tool.
java_binary_host {
name: "hoststubgen",
- main_class: "com.android.hoststubgen.HostStubGenMain",
+ main_class: "com.android.hoststubgen.Main",
+ srcs: ["src/**/*.kt"],
static_libs: [
- "hoststubgen-lib",
+ "hoststubgen-helper-runtime",
"junit",
"ow2-asm",
"ow2-asm-analysis",
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 803dc283b8c7..1089f82b6472 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -32,6 +32,7 @@ import com.android.hoststubgen.visitors.PackageRedirectRemapper
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.util.CheckClassAdapter
import java.io.BufferedInputStream
import java.io.FileOutputStream
@@ -51,7 +52,7 @@ class HostStubGen(val options: HostStubGenOptions) {
val stats = HostStubGenStats()
// Load all classes.
- val allClasses = ClassNodes.loadClassStructures(options.inJar.get)
+ val allClasses = loadClassStructures(options.inJar.get)
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
@@ -91,6 +92,55 @@ class HostStubGen(val options: HostStubGenOptions) {
}
/**
+ * Load all the classes, without code.
+ */
+ private fun loadClassStructures(inJar: String): ClassNodes {
+ log.i("Reading class structure from $inJar ...")
+ val start = System.currentTimeMillis()
+
+ val allClasses = ClassNodes()
+
+ log.withIndent {
+ ZipFile(inJar).use { inZip ->
+ val inEntries = inZip.entries()
+
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ if (entry.name.endsWith(".class")) {
+ val cr = ClassReader(bis)
+ val cn = ClassNode()
+ cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES)
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
+ }
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$inJar is not a desktop jar file. It contains a *.dex file.")
+ } else {
+ // Unknown file type. Skip.
+ while (bis.available() > 0) {
+ bis.skip((1024 * 1024).toLong())
+ }
+ }
+ }
+ }
+ }
+ }
+ if (allClasses.size == 0) {
+ log.w("$inJar contains no *.class files.")
+ }
+
+ val end = System.currentTimeMillis()
+ log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
+ return allClasses
+ }
+
+ /**
* Build the filter, which decides what classes/methods/fields should be put in stub or impl
* jars, and "how". (e.g. with substitution?)
*/
@@ -179,7 +229,7 @@ class HostStubGen(val options: HostStubGenOptions) {
val intersectingJars = mutableMapOf<String, ClassNodes>()
filenames.forEach { filename ->
- intersectingJars[filename] = ClassNodes.loadClassStructures(filename)
+ intersectingJars[filename] = loadClassStructures(filename)
}
return intersectingJars
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 9ff798a4b5cb..9f5d524517d0 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -268,7 +268,7 @@ class HostStubGenOptions(
}
if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
- " $executableName will not generate jar files.")
+ " $COMMAND_NAME will not generate jar files.")
}
if (ret.enableNonStubMethodCallDetection.get) {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
index 45e7e301c0d1..4882c00d2b3c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
@@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:JvmName("HostStubGenMain")
+@file:JvmName("Main")
package com.android.hoststubgen
import java.io.PrintWriter
+const val COMMAND_NAME = "HostStubGen"
+
/**
* Entry point.
*/
fun main(args: Array<String>) {
- executableName = "HostStubGen"
-
var success = false
var clanupOnError = false
@@ -33,7 +33,7 @@ fun main(args: Array<String>) {
val options = HostStubGenOptions.parseArgs(args)
clanupOnError = options.cleanUpOnError.get
- log.v("$executableName started")
+ log.v("HostStubGen started")
log.v("Options: $options")
// Run.
@@ -41,7 +41,7 @@ fun main(args: Array<String>) {
success = true
} catch (e: Throwable) {
- log.e("$executableName: Error: ${e.message}")
+ log.e("$COMMAND_NAME: Error: ${e.message}")
if (e !is UserErrorException) {
e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
}
@@ -49,7 +49,7 @@ fun main(args: Array<String>) {
TODO("Remove output jars here")
}
} finally {
- log.i("$executableName finished")
+ log.i("$COMMAND_NAME finished")
log.flush()
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
index aa63d8d9f870..937e56c2cbb5 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -16,11 +16,6 @@
package com.android.hoststubgen
/**
- * Name of this executable. Set it in the main method.
- */
-var executableName = "[command name not set]"
-
-/**
* A regex that maches whitespate.
*/
val whitespaceRegex = """\s+""".toRegex()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 83e122feeeb2..0579c2bb0525 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -34,9 +34,6 @@ val CLASS_INITIALIZER_NAME = "<clinit>"
/** Descriptor of the class initializer method. */
val CLASS_INITIALIZER_DESC = "()V"
-/** Name of constructors. */
-val CTOR_NAME = "<init>"
-
/**
* Find any of [anyAnnotations] from the list of visible / invisible annotations.
*/
@@ -138,10 +135,10 @@ fun writeByteCodeToPushArguments(
// Note, long and double will consume two local variable spaces, so the extra `i++`.
when (type) {
Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected")
- Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
+ Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
-> writer.visitVarInsn(Opcodes.ILOAD, i)
- Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i)
Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++)
+ Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i)
Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++)
else -> writer.visitVarInsn(Opcodes.ALOAD, i)
}
@@ -157,10 +154,10 @@ fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) {
// See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
when (type) {
Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN)
- Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
+ Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
-> writer.visitInsn(Opcodes.IRETURN)
- Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN)
Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
+ Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN)
Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN)
else -> writer.visitInsn(Opcodes.ARETURN)
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index 92906a75b93a..bc34ef0dc8a7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -16,18 +16,13 @@
package com.android.hoststubgen.asm
import com.android.hoststubgen.ClassParseException
-import com.android.hoststubgen.InvalidJarFileException
-import com.android.hoststubgen.log
-import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.TypeAnnotationNode
-import java.io.BufferedInputStream
import java.io.PrintWriter
import java.util.Arrays
-import java.util.zip.ZipFile
/**
* Stores all classes loaded from a jar file, in a form of [ClassNode]
@@ -67,8 +62,8 @@ class ClassNodes {
/** Find a field, which may not exist. */
fun findField(
- className: String,
- fieldName: String,
+ className: String,
+ fieldName: String,
): FieldNode? {
return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn ->
return fn
@@ -77,14 +72,14 @@ class ClassNodes {
/** Find a method, which may not exist. */
fun findMethod(
- className: String,
- methodName: String,
- descriptor: String,
+ className: String,
+ methodName: String,
+ descriptor: String,
): MethodNode? {
return findClass(className)?.methods
- ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
- return mn
- }
+ ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
+ return mn
+ }
}
/** @return true if a class has a class initializer. */
@@ -111,33 +106,26 @@ class ClassNodes {
private fun dumpClass(pw: PrintWriter, cn: ClassNode) {
pw.printf("Class: %s [access: %x]\n", cn.name, cn.access)
- dumpAnnotations(
- pw, " ",
- cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
- cn.visibleAnnotations, cn.invisibleAnnotations,
- )
+ dumpAnnotations(pw, " ",
+ cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
+ cn.visibleAnnotations, cn.invisibleAnnotations,
+ )
for (f in cn.fields ?: emptyList()) {
- pw.printf(
- " Field: %s [sig: %s] [desc: %s] [access: %x]\n",
- f.name, f.signature, f.desc, f.access
- )
- dumpAnnotations(
- pw, " ",
- f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
- f.visibleAnnotations, f.invisibleAnnotations,
- )
+ pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n",
+ f.name, f.signature, f.desc, f.access)
+ dumpAnnotations(pw, " ",
+ f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
+ f.visibleAnnotations, f.invisibleAnnotations,
+ )
}
for (m in cn.methods ?: emptyList()) {
- pw.printf(
- " Method: %s [sig: %s] [desc: %s] [access: %x]\n",
- m.name, m.signature, m.desc, m.access
- )
- dumpAnnotations(
- pw, " ",
- m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
- m.visibleAnnotations, m.invisibleAnnotations,
- )
+ pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n",
+ m.name, m.signature, m.desc, m.access)
+ dumpAnnotations(pw, " ",
+ m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
+ m.visibleAnnotations, m.invisibleAnnotations,
+ )
}
}
@@ -148,7 +136,7 @@ class ClassNodes {
invisibleTypeAnnotations: List<TypeAnnotationNode>?,
visibleAnnotations: List<AnnotationNode>?,
invisibleAnnotations: List<AnnotationNode>?,
- ) {
+ ) {
for (an in visibleTypeAnnotations ?: emptyList()) {
pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc)
}
@@ -178,55 +166,4 @@ class ClassNodes {
}
}
}
-
- companion object {
- /**
- * Load all the classes, without code.
- */
- fun loadClassStructures(inJar: String): ClassNodes {
- log.i("Reading class structure from $inJar ...")
- val start = System.currentTimeMillis()
-
- val allClasses = ClassNodes()
-
- log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
-
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- if (entry.name.endsWith(".class")) {
- val cr = ClassReader(bis)
- val cn = ClassNode()
- cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES)
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file. It contains a *.dex file.")
- } else {
- // Unknown file type. Skip.
- while (bis.available() > 0) {
- bis.skip((1024 * 1024).toLong())
- }
- }
- }
- }
- }
- }
- if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
- }
-
- val end = System.currentTimeMillis()
- log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
- return allClasses
- }
- }
} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 5a26fc69d473..78b13fd36f06 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -19,14 +19,14 @@ import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.HostStubGenInternalException
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.isAnonymousInnerClass
+import com.android.hoststubgen.log
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.isAnnotation
-import com.android.hoststubgen.asm.isAnonymousInnerClass
import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
import com.android.hoststubgen.asm.isEnum
import com.android.hoststubgen.asm.isSynthetic
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
-import com.android.hoststubgen.log
import org.objectweb.asm.tree.ClassNode
/**
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index fa8fe6cd384f..f70a17d9b7cd 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -1833,7 +1833,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 11, attributes: 2
+ interfaces: 0, fields: 1, methods: 10, attributes: 2
int value;
descriptor: I
flags: (0x0000)
@@ -1938,10 +1938,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
x: athrow
LineNumberTable:
-
- public static native byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
@@ -1959,7 +1955,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 2
+ interfaces: 0, fields: 0, methods: 4, attributes: 2
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2017,22 +2013,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
Start Length Slot Name Signature
0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
0 7 1 arg I
-
- public static byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=2, args_size=2
- x: iload_0
- x: iload_1
- x: iadd
- x: i2b
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 arg1 B
- 0 5 1 arg2 B
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index c605f767f527..37de857b9780 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -1554,7 +1554,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 3
+ interfaces: 0, fields: 1, methods: 9, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -1686,15 +1686,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 11d5939b7917..c9c607c58c68 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -2236,7 +2236,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 11, attributes: 3
+ interfaces: 0, fields: 1, methods: 10, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -2435,23 +2435,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=2, args_size=2
- x: iload_0
- x: iload_1
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
- x: ireturn
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -2474,7 +2457,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2568,31 +2551,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iload_0
- x: iload_1
- x: iadd
- x: i2b
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 15 5 0 arg1 B
- 15 5 1 arg2 B
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index c605f767f527..37de857b9780 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -1554,7 +1554,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 3
+ interfaces: 0, fields: 1, methods: 9, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -1686,15 +1686,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static native byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 088bc80e11c5..a57907d9398b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -2743,7 +2743,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 12, attributes: 3
+ interfaces: 0, fields: 1, methods: 11, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -3002,28 +3002,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: iload_0
- x: iload_1
- x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
- x: ireturn
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -3046,7 +3024,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 6, attributes: 3
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -3170,36 +3148,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
- public static byte nativeBytePlus(byte, byte);
- descriptor: (BB)B
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- x: ldc #x // String nativeBytePlus
- x: ldc #x // String (BB)B
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- x: iload_0
- x: iload_1
- x: iadd
- x: i2b
- x: ireturn
- LineNumberTable:
- LocalVariableTable:
- Start Length Slot Name Signature
- 26 5 0 arg1 B
- 26 5 1 arg2 B
- RuntimeVisibleAnnotations:
- x: #x()
- com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index 09ee183a2dcc..5a5e22db59e5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -52,6 +52,4 @@ public class TinyFrameworkNative {
public static void nativeStillNotSupported_should_be_like_this() {
throw new RuntimeException();
}
-
- public static native byte nativeBytePlus(byte arg1, byte arg2);
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
index b23c21602967..749ebaa378e3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
@@ -34,8 +34,4 @@ public class TinyFrameworkNative_host {
public static int nativeNonStaticAddToValue(TinyFrameworkNative source, int arg) {
return source.value + arg;
}
-
- public static byte nativeBytePlus(byte arg1, byte arg2) {
- return (byte) (arg1 + arg2);
- }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 762180dcf74b..ba17c75132f2 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -154,22 +154,13 @@ public class TinyFrameworkClassTest {
}
@Test
- public void testNativeSubstitutionLong() {
- assertThat(TinyFrameworkNative.nativeLongPlus(1L, 2L)).isEqualTo(3L);
- }
-
- @Test
- public void testNativeSubstitutionByte() {
- assertThat(TinyFrameworkNative.nativeBytePlus((byte) 3, (byte) 4)).isEqualTo(7);
- }
-
- @Test
public void testNativeSubstitutionClass_nonStatic() {
TinyFrameworkNative instance = new TinyFrameworkNative();
instance.setValue(5);
assertThat(instance.nativeNonStaticAddToValue(3)).isEqualTo(8);
}
+
@Test
public void testSubstituteNativeWithThrow() throws Exception {
// We can't use TinyFrameworkNative.nativeStillNotSupported() directly in this class,